โ–ฅNYC

powershell-master

SKILL.md

PowerShell Master

๐Ÿšจ CRITICAL GUIDELINES

Windows File Path Requirements

MANDATORY: Always Use Backslashes on Windows for File Paths

When using Edit or Write tools on Windows, you MUST use backslashes (\) in file paths, NOT forward slashes (/).

Examples:

  • โŒ WRONG: D:/repos/project/file.tsx
  • โœ… CORRECT: D:\repos\project\file.tsx

This applies to:

  • Edit tool file_path parameter
  • Write tool file_path parameter
  • All file operations on Windows systems

Documentation Guidelines

NEVER create new documentation files unless explicitly requested by the user.

  • Priority: Update existing README.md files rather than creating new documentation
  • Repository cleanliness: Keep repository root clean - only README.md unless user requests otherwise
  • Style: Documentation should be concise, direct, and professional - avoid AI-generated tone
  • User preference: Only create additional .md files when user specifically asks for documentation

Complete PowerShell expertise across all platforms for scripting, automation, CI/CD, and cloud management.


๐ŸŽฏ When to Activate

PROACTIVELY activate for ANY PowerShell-related task:

  • โœ… PowerShell Scripts - Creating, reviewing, optimizing any .ps1 file
  • โœ… Cmdlets & Modules - Finding, installing, using any PowerShell modules
  • โœ… Cross-Platform - Windows, Linux, macOS PowerShell tasks
  • โœ… CI/CD Integration - GitHub Actions, Azure DevOps, Bitbucket Pipelines
  • โœ… Cloud Automation - Azure (Az), AWS, Microsoft 365 (Microsoft.Graph)
  • โœ… Module Management - PSGallery search, installation, updates
  • โœ… Script Debugging - Troubleshooting, performance, security
  • โœ… Best Practices - Code quality, standards, production-ready scripts

๐Ÿ“‹ PowerShell Overview

PowerShell Versions & Platforms

PowerShell 7+ (Recommended)

  • Cross-platform: Windows, Linux, macOS
  • Open source, actively developed
  • Better performance than PowerShell 5.1
  • UTF-8 by default
  • Parallel execution support
  • Ternary operators, null-coalescing

Windows PowerShell 5.1 (Legacy)

  • Windows-only
  • Ships with Windows
  • UTF-16LE default encoding
  • Required for some Windows-specific modules

Installation Locations:

  • Windows: C:\Program Files\PowerShell\7\ (PS7) or C:\Windows\System32\WindowsPowerShell\v1.0\ (5.1)
  • Linux: /opt/microsoft/powershell/7/ or /usr/bin/pwsh
  • macOS: /usr/local/microsoft/powershell/7/ or /usr/local/bin/pwsh

๐Ÿ”ง Cross-Platform Best Practices

1. Path Handling

DO:

# Use Join-Path for cross-platform paths
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"

# Use [System.IO.Path] for path manipulation
$fullPath = [System.IO.Path]::Combine($home, "documents", "file.txt")

# Forward slashes work on all platforms in PowerShell 7+
$path = "$PSScriptRoot/subfolder/file.txt"

DON'T:

# Hardcoded backslashes (Windows-only)
$path = "C:\Users\Name\file.txt"

# Assume case-insensitive file systems
Get-ChildItem "MyFile.txt"  # Works on Windows, fails on Linux/macOS if casing is wrong

2. Platform Detection

# Use automatic variables
if ($IsWindows) {
    # Windows-specific code
    $env:Path -split ';'
}
elseif ($IsLinux) {
    # Linux-specific code
    $env:PATH -split ':'
}
elseif ($IsMacOS) {
    # macOS-specific code
    $env:PATH -split ':'
}

# Check PowerShell version
if ($PSVersionTable.PSVersion.Major -ge 7) {
    # PowerShell 7+ features
}

3. Avoid Aliases in Scripts

# DON'T use aliases (they may differ across platforms)
ls | ? {$_.Length -gt 1MB} | % {$_.Name}

# DO use full cmdlet names
Get-ChildItem | Where-Object {$_.Length -gt 1MB} | ForEach-Object {$_.Name}

Why: On Linux/macOS, aliases might invoke native commands instead of PowerShell cmdlets, causing unexpected results.

4. Text Encoding

# PowerShell 7+ uses UTF-8 by default
"Hello" | Out-File -FilePath output.txt

# For PowerShell 5.1 compatibility, specify encoding
"Hello" | Out-File -FilePath output.txt -Encoding UTF8

# Best practice: Always specify encoding for cross-platform scripts
$content | Set-Content -Path $file -Encoding UTF8NoBOM

5. Environment Variables (Cross-Platform)

# BEST PRACTICE: Use .NET Environment class for cross-platform compatibility
[Environment]::UserName      # Works on all platforms
[Environment]::MachineName   # Works on all platforms
[IO.Path]::GetTempPath()     # Works on all platforms

# AVOID: These are platform-specific
$env:USERNAME                # Windows only
$env:USER                    # Linux/macOS only

# Environment variable names are CASE-SENSITIVE on Linux/macOS
$env:PATH    # Correct on Linux/macOS
$env:Path    # May not work on Linux/macOS

6. Shell Detection (Windows: PowerShell vs Git Bash)

CRITICAL: On Windows, distinguish between PowerShell and Git Bash/MSYS2 environments:

# PowerShell detection (most reliable)
if ($env:PSModulePath -and ($env:PSModulePath -split ';').Count -ge 3) {
    Write-Host "Running in PowerShell"
}

# Platform-specific automatic variables (PowerShell 7+)
if ($IsWindows) {
    # Windows-specific code
}
elseif ($IsLinux) {
    # Linux-specific code
}
elseif ($IsMacOS) {
    # macOS-specific code
}

Git Bash/MSYS2 Detection:

# Bash detection - check MSYSTEM environment variable
if [ -n "$MSYSTEM" ]; then
    echo "Running in Git Bash/MSYS2: $MSYSTEM"
    # MSYSTEM values: MINGW64, MINGW32, MSYS
fi

When to Use Each Shell:

  • PowerShell: Windows automation, Azure/M365, PSGallery modules, object pipelines
  • Git Bash: Git operations, Unix tools (sed/awk/grep), POSIX scripts, text processing

Path Handling Differences:

  • PowerShell: C:\Users\John or C:/Users/John (both work in PS 7+)
  • Git Bash: /c/Users/John (Unix-style, auto-converts to Windows when calling Windows tools)

See powershell-shell-detection skill for comprehensive cross-shell guidance.

7. Line Endings

# PowerShell handles line endings automatically
# But be explicit for git or cross-platform tools
git config core.autocrlf input  # Linux/macOS
git config core.autocrlf true   # Windows

๐Ÿ“ฆ Module Management (PSResourceGet & PSGallery)

PSResourceGet - Modern Package Manager (2025)

PSResourceGet is 2x faster than PowerShellGet and actively maintained:

# PSResourceGet ships with PowerShell 7.4+ (or install manually)
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Force

# Modern commands (PSResourceGet)
Install-PSResource -Name Az -Scope CurrentUser        # 2x faster
Find-PSResource -Name "*Azure*"                       # Faster search
Update-PSResource -Name Az                            # Batch updates
Get-InstalledPSResource                               # List installed
Uninstall-PSResource -Name OldModule                  # Clean uninstall

# Compatibility: Your old Install-Module commands still work
# They automatically call PSResourceGet internally
Install-Module -Name Az -Scope CurrentUser            # Works, uses PSResourceGet

Finding Modules

# PSResourceGet (Modern)
Find-PSResource -Name "*Azure*"
Find-PSResource -Tag "Security"
Find-PSResource -Name Az | Select-Object Name, Version, PublishedDate

# Legacy PowerShellGet (still works)
Find-Module -Name "*Azure*"
Find-Command -Name Get-AzVM

Installing Modules

# RECOMMENDED: PSResourceGet (2x faster)
Install-PSResource -Name Az -Scope CurrentUser -TrustRepository
Install-PSResource -Name Microsoft.Graph -Version 2.32.0

# Legacy: PowerShellGet (slower, but still works)
Install-Module -Name Az -Scope CurrentUser -Force
Install-Module -Name Pester -Scope AllUsers  # Requires elevation

Managing Installed Modules

# List installed (PSResourceGet)
Get-InstalledPSResource
Get-InstalledPSResource -Name Az

# Update modules (PSResourceGet)
Update-PSResource -Name Az
Update-PSResource                              # Updates all

# Uninstall (PSResourceGet)
Uninstall-PSResource -Name OldModule -AllVersions

# Import module
Import-Module -Name Az.Accounts

Offline Installation

# Save module (works with both)
Save-PSResource -Name Az -Path C:\OfflineModules
# Or: Save-Module -Name Az -Path C:\OfflineModules

# Install from saved location
Install-PSResource -Name Az -Path C:\OfflineModules

๐ŸŒŸ Popular PowerShell Modules

Azure (Az Module 14.5.0)

Latest: Az 14.5.0 (October 2025) with zone redundancy and symbolic links

# Install Azure module 14.5.0
Install-PSResource -Name Az -Scope CurrentUser
# Or: Install-Module -Name Az -Scope CurrentUser -Force

# Connect to Azure
Connect-AzAccount

# Common operations
Get-AzVM
Get-AzResourceGroup
New-AzResourceGroup -Name "MyRG" -Location "EastUS"

# NEW in Az 14.5: Zone redundancy for storage
New-AzStorageAccount -ResourceGroupName "MyRG" -Name "storage123" `
    -Location "EastUS" -SkuName "Standard_LRS" -EnableZoneRedundancy

# NEW in Az 14.5: Symbolic links in NFS File Share
New-AzStorageFileSymbolicLink -Context $ctx -ShareName "nfsshare" `
    -Path "symlink" -Target "/target/path"

Key Submodules:

  • Az.Accounts - Authentication (MFA required Sep 2025+)
  • Az.Compute - VMs, scale sets
  • Az.Storage - Storage accounts (zone redundancy support)
  • Az.Network - Virtual networks, NSGs
  • Az.KeyVault - Key Vault operations
  • Az.Resources - Resource groups, deployments

Microsoft Graph (Microsoft.Graph 2.32.0)

CRITICAL: MSOnline and AzureAD modules retired (March-May 2025). Use Microsoft.Graph instead.

# Install Microsoft Graph 2.32.0 (October 2025)
Install-PSResource -Name Microsoft.Graph -Scope CurrentUser
# Or: Install-Module -Name Microsoft.Graph -Scope CurrentUser

# Connect with required scopes
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All"

# Common operations
Get-MgUser
Get-MgGroup
New-MgUser -DisplayName "John Doe" -UserPrincipalName "john@domain.com" -MailNickname "john"
Get-MgTeam

# Migration from AzureAD/MSOnline
# OLD: Connect-AzureAD / Connect-MsolService
# NEW: Connect-MgGraph
# OLD: Get-AzureADUser / Get-MsolUser
# NEW: Get-MgUser

PnP PowerShell (SharePoint/Teams)

# Install PnP PowerShell
Install-Module -Name PnP.PowerShell -Scope CurrentUser

# Connect to SharePoint Online
Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/site" -Interactive

# Common operations
Get-PnPList
Get-PnPFile -Url "/sites/site/Shared Documents/file.docx"
Add-PnPListItem -List "Tasks" -Values @{"Title"="New Task"}

AWS Tools for PowerShell

# Install AWS Tools
Install-Module -Name AWS.Tools.Installer -Force
Install-AWSToolsModule AWS.Tools.EC2,AWS.Tools.S3

# Configure credentials
Set-AWSCredential -AccessKey $accessKey -SecretKey $secretKey -StoreAs default

# Common operations
Get-EC2Instance
Get-S3Bucket
New-S3Bucket -BucketName "my-bucket"

Other Popular Modules

# Pester (Testing framework)
Install-Module -Name Pester -Force

# PSScriptAnalyzer (Code analysis)
Install-Module -Name PSScriptAnalyzer

# ImportExcel (Excel manipulation without Excel)
Install-Module -Name ImportExcel

# PowerShellGet 3.x (Modern package management)
Install-Module -Name Microsoft.PowerShell.PSResourceGet

๐Ÿš€ CI/CD Integration

GitHub Actions

name: PowerShell CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install PowerShell modules
        shell: pwsh
        run: |
          Install-Module -Name Pester -Force -Scope CurrentUser
          Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser

      - name: Run Pester tests
        shell: pwsh
        run: |
          Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml

      - name: Run PSScriptAnalyzer
        shell: pwsh
        run: |
          Invoke-ScriptAnalyzer -Path . -Recurse -ReportSummary

Multi-Platform Matrix:

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Test on ${{ matrix.os }}
        shell: pwsh
        run: |
          ./test-script.ps1

Azure DevOps Pipelines

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      Install-Module -Name Pester -Force -Scope CurrentUser
      Invoke-Pester -Path ./tests -OutputFormat NUnitXml
  displayName: 'Run Pester Tests'

- task: PowerShell@2
  inputs:
    filePath: '$(System.DefaultWorkingDirectory)/build.ps1'
    arguments: '-Configuration Release'
  displayName: 'Run Build Script'

- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'NUnit'
    testResultsFiles: '**/TestResults.xml'

Cross-Platform Pipeline:

strategy:
  matrix:
    linux:
      imageName: 'ubuntu-latest'
    windows:
      imageName: 'windows-latest'
    mac:
      imageName: 'macos-latest'

pool:
  vmImage: $(imageName)

steps:
- pwsh: |
    Write-Host "Running on $($PSVersionTable.OS)"
    ./test-script.ps1
  displayName: 'Cross-platform test'

Bitbucket Pipelines

image: mcr.microsoft.com/powershell:latest

pipelines:
  default:
    - step:
        name: Test with PowerShell
        script:
          - pwsh -Command "Install-Module -Name Pester -Force"
          - pwsh -Command "Invoke-Pester -Path ./tests"

    - step:
        name: Deploy
        deployment: production
        script:
          - pwsh -File ./deploy.ps1

๐Ÿ’ป PowerShell Syntax & Cmdlets

Cmdlet Structure

# Verb-Noun pattern
Get-ChildItem
Set-Location
New-Item
Remove-Item

# Common parameters (available on all cmdlets)
Get-Process -Verbose
Set-Content -Path file.txt -WhatIf
Remove-Item -Path folder -Confirm
Invoke-RestMethod -Uri $url -ErrorAction Stop

Variables & Data Types

# Variables (loosely typed)
$string = "Hello World"
$number = 42
$array = @(1, 2, 3, 4, 5)
$hashtable = @{Name="John"; Age=30}

# Strongly typed
[string]$name = "John"
[int]$age = 30
[datetime]$date = Get-Date

# Special variables
$PSScriptRoot  # Directory containing the script
$PSCommandPath  # Full path to the script
$args  # Script arguments
$_  # Current pipeline object

Operators

# Comparison operators
-eq  # Equal
-ne  # Not equal
-gt  # Greater than
-lt  # Less than
-match  # Regex match
-like  # Wildcard match
-contains  # Array contains

# Logical operators
-and
-or
-not

# PowerShell 7+ ternary operator
$result = $condition ? "true" : "false"

# Null-coalescing (PS 7+)
$value = $null ?? "default"

Control Flow

# If-ElseIf-Else
if ($condition) {
    # Code
} elseif ($otherCondition) {
    # Code
} else {
    # Code
}

# Switch
switch ($value) {
    1 { "One" }
    2 { "Two" }
    {$_ -gt 10} { "Greater than 10" }
    default { "Other" }
}

# Loops
foreach ($item in $collection) {
    # Process item
}

for ($i = 0; $i -lt 10; $i++) {
    # Loop code
}

while ($condition) {
    # Loop code
}

do {
    # Loop code
} while ($condition)

Functions

function Get-Something {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$Name,

        [Parameter()]
        [int]$Count = 1,

        [Parameter(ValueFromPipeline=$true)]
        [string[]]$InputObject
    )

    begin {
        # Initialization
    }

    process {
        # Process each pipeline object
        foreach ($item in $InputObject) {
            # Work with $item
        }
    }

    end {
        # Cleanup
        return $result
    }
}

Pipeline & Filtering

# Pipeline basics
Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, CPU

# Simplified syntax (PS 3.0+)
Get-Process | Where CPU -gt 100 | Select Name, CPU

# ForEach-Object
Get-ChildItem | ForEach-Object {
    Write-Host $_.Name
}

# Simplified (PS 4.0+)
Get-ChildItem | % Name

# Group, Sort, Measure
Get-Process | Group-Object ProcessName
Get-Service | Sort-Object Status
Get-ChildItem | Measure-Object -Property Length -Sum

Error Handling

# Try-Catch-Finally
try {
    Get-Content -Path "nonexistent.txt" -ErrorAction Stop
}
catch [System.IO.FileNotFoundException] {
    Write-Error "File not found"
}
catch {
    Write-Error "An error occurred: $_"
}
finally {
    # Cleanup code
}

# Error action preference
$ErrorActionPreference = "Stop"  # Treat all errors as terminating
$ErrorActionPreference = "Continue"  # Default
$ErrorActionPreference = "SilentlyContinue"  # Suppress errors

๐Ÿ”’ Security Best Practices (2025 Standards)

Modern Security Framework (JEA + WDAC + Logging)

2025 Security Requirements:

  1. JEA - Just Enough Administration for role-based access
  2. WDAC - Windows Defender Application Control for script approval
  3. Constrained Language Mode - For non-admin users
  4. Script Block Logging - For audit trails

Just Enough Administration (JEA)

Required for production environments in 2025:

# Create JEA session configuration file
New-PSSessionConfigurationFile -SessionType RestrictedRemoteServer `
    -Path "C:\JEA\HelpDesk.pssc" `
    -VisibleCmdlets @{
        Name = 'Restart-Service'
        Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler', 'Wuauserv' }
    }, @{
        Name = 'Get-Service'
    } `
    -LanguageMode NoLanguage `
    -ExecutionPolicy RemoteSigned

# Register JEA endpoint
Register-PSSessionConfiguration -Name HelpDesk `
    -Path "C:\JEA\HelpDesk.pssc" `
    -Force

# Connect with limited privileges
Enter-PSSession -ComputerName Server01 -ConfigurationName HelpDesk

Windows Defender Application Control (WDAC)

Replaces AppLocker for PowerShell script control:

# Create WDAC policy for approved scripts
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
    -ScanPath "C:\ApprovedScripts" `
    -Level FilePublisher `
    -Fallback Hash

# Convert to binary
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
    -BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"

# Deploy via Group Policy or MDM

Constrained Language Mode

Recommended for all non-admin users:

# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)

# Enable system-wide via environment variable
[Environment]::SetEnvironmentVariable(
    "__PSLockdownPolicy",
    "4",
    [System.EnvironmentVariableTarget]::Machine
)

Script Block Logging

Enable for security auditing:

# Enable via Group Policy or Registry
# HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
# EnableScriptBlockLogging = 1
# EnableScriptBlockInvocationLogging = 1

# Check logs
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
    Where-Object Id -eq 4104 |  # Script Block Logging
    Select-Object TimeCreated, Message -First 10

Execution Policy

# Check current execution policy
Get-ExecutionPolicy

# Set for current user (no admin needed)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

# Bypass for single session (use sparingly)
pwsh -ExecutionPolicy Bypass -File script.ps1

Credential Management

# NEVER hardcode credentials
# BAD: $password = "MyP@ssw0rd"

# Use SecretManagement module (modern approach)
Install-PSResource -Name Microsoft.PowerShell.SecretManagement
Install-PSResource -Name SecretManagement.KeyVault

Register-SecretVault -Name AzureKeyVault -ModuleName SecretManagement.KeyVault
$secret = Get-Secret -Name "DatabasePassword" -Vault AzureKeyVault

# Legacy: Get-Credential for interactive
$cred = Get-Credential

# Azure Key Vault for production
$vaultName = "MyKeyVault"
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name "DatabasePassword"
$secret.SecretValue

Input Validation

function Do-Something {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter()]
        [ValidateRange(1, 100)]
        [int]$Count,

        [Parameter()]
        [ValidateSet("Option1", "Option2", "Option3")]
        [string]$Option,

        [Parameter()]
        [ValidatePattern('^\d{3}-\d{3}-\d{4}$')]
        [string]$PhoneNumber
    )
}

Code Signing (Production)

# Get code signing certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert

# Sign script
Set-AuthenticodeSignature -FilePath script.ps1 -Certificate $cert

โšก Performance Optimization

PowerShell 7+ Features

# Parallel ForEach (PS 7+)
1..10 | ForEach-Object -Parallel {
    Start-Sleep -Seconds 1
    "Processed $_"
} -ThrottleLimit 5

# Ternary operator
$result = $value ? "true" : "false"

# Null-coalescing
$name = $userName ?? "default"

# Null-conditional member access
$length = $string?.Length

Efficient Filtering

# Use .NET methods for performance
# Instead of: Get-Content large.txt | Where-Object {$_ -match "pattern"}
[System.IO.File]::ReadLines("large.txt") | Where-Object {$_ -match "pattern"}

# Use -Filter parameter when available
Get-ChildItem -Path C:\ -Filter *.log -Recurse
# Instead of: Get-ChildItem -Path C:\ -Recurse | Where-Object {$_.Extension -eq ".log"}

ArrayList vs Array

# Arrays are immutable - slow for additions
$array = @()
1..1000 | ForEach-Object { $array += $_ }  # SLOW

# Use ArrayList for dynamic collections
$list = [System.Collections.ArrayList]::new()
1..1000 | ForEach-Object { [void]$list.Add($_) }  # FAST

# Or use generic List
$list = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $list.Add($_) }

๐Ÿงช Testing with Pester

# Install Pester
Install-Module -Name Pester -Force

# Basic test structure
Describe "Get-Something Tests" {
    Context "When input is valid" {
        It "Should return expected value" {
            $result = Get-Something -Name "Test"
            $result | Should -Be "Expected"
        }
    }

    Context "When input is invalid" {
        It "Should throw an error" {
            { Get-Something -Name $null } | Should -Throw
        }
    }
}

# Run tests
Invoke-Pester -Path ./tests
Invoke-Pester -Path ./tests -OutputFormat NUnitXml -OutputFile TestResults.xml

# Code coverage
Invoke-Pester -Path ./tests -CodeCoverage ./src/*.ps1

๐Ÿ“ Script Requirements & Versioning

# Require specific PowerShell version
#Requires -Version 7.0

# Require modules
#Requires -Modules Az.Accounts, Az.Compute

# Require admin/elevated privileges (Windows)
#Requires -RunAsAdministrator

# Combine multiple requirements
#Requires -Version 7.0
#Requires -Modules @{ModuleName='Pester'; ModuleVersion='5.0.0'}

# Use strict mode
Set-StrictMode -Version Latest

๐ŸŽ“ Common Cmdlets Reference

File System

Get-ChildItem (gci, ls, dir)
Set-Location (cd, sl)
New-Item (ni)
Remove-Item (rm, del)
Copy-Item (cp, copy)
Move-Item (mv, move)
Rename-Item (rn, ren)
Get-Content (gc, cat, type)
Set-Content (sc)
Add-Content (ac)

Process Management

Get-Process (ps, gps)
Stop-Process (kill, spps)
Start-Process (start, saps)
Wait-Process

Service Management

Get-Service (gsv)
Start-Service (sasv)
Stop-Service (spsv)
Restart-Service (srsv)
Set-Service

Network

Test-Connection (ping)
Test-NetConnection
Invoke-WebRequest (curl, wget, iwr)
Invoke-RestMethod (irm)

Object Manipulation

Select-Object (select)
Where-Object (where, ?)
ForEach-Object (foreach, %)
Sort-Object (sort)
Group-Object (group)
Measure-Object (measure)
Compare-Object (compare, diff)

๐ŸŒ REST API & Web Requests

# GET request
$response = Invoke-RestMethod -Uri "https://api.example.com/data" -Method Get

# POST with JSON body
$body = @{
    name = "John"
    age = 30
} | ConvertTo-Json

$response = Invoke-RestMethod -Uri "https://api.example.com/users" `
    -Method Post -Body $body -ContentType "application/json"

# With headers and authentication
$headers = @{
    "Authorization" = "Bearer $token"
    "Accept" = "application/json"
}

$response = Invoke-RestMethod -Uri $url -Headers $headers

# Download file
Invoke-WebRequest -Uri $url -OutFile "file.zip"

๐Ÿ—๏ธ Script Structure Best Practices

<#
.SYNOPSIS
    Brief description

.DESCRIPTION
    Detailed description

.PARAMETER Name
    Parameter description

.EXAMPLE
    PS> .\script.ps1 -Name "John"
    Example usage

.NOTES
    Author: Your Name
    Version: 1.0.0
    Date: 2025-01-01
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$Name
)

# Script-level error handling
$ErrorActionPreference = "Stop"

# Use strict mode
Set-StrictMode -Version Latest

try {
    # Main script logic
    Write-Verbose "Starting script"

    # ... script code ...

    Write-Verbose "Script completed successfully"
}
catch {
    Write-Error "Script failed: $_"
    exit 1
}
finally {
    # Cleanup
}

๐Ÿ“š Additional Resources

Official Documentation

Module Discovery

# Find modules by keyword
Find-Module -Tag "Azure"
Find-Module -Tag "Security"

# Explore commands in a module
Get-Command -Module Az.Compute
Get-Command -Verb Get -Noun *VM*

# Get command help
Get-Help Get-AzVM -Full
Get-Help Get-AzVM -Examples
Get-Help Get-AzVM -Online

Update Help System

# Update help files (requires internet)
Update-Help -Force -ErrorAction SilentlyContinue

# Update help for specific modules
Update-Help -Module Az -Force

๐ŸŽฏ Quick Decision Guide

Use PowerShell 7+ when:

  • Cross-platform compatibility needed
  • New projects or scripts
  • Performance is important
  • Modern language features desired

Use Windows PowerShell 5.1 when:

  • Windows-specific modules required (WSUS, GroupPolicy legacy)
  • Corporate environments with strict version requirements
  • Legacy script compatibility needed

Choose Azure CLI when:

  • Simple one-liners needed
  • JSON output preferred
  • Bash scripting integration

Choose PowerShell Az module when:

  • Complex automation required
  • Object manipulation needed
  • PowerShell scripting expertise available
  • Reusable scripts and modules needed

โœ… Pre-Flight Checklist for Scripts

Before running any PowerShell script, ensure:

  1. โœ… Platform Detection - Use $IsWindows, $IsLinux, $IsMacOS
  2. โœ… Version Check - #Requires -Version 7.0 if needed
  3. โœ… Module Requirements - #Requires -Modules specified
  4. โœ… Error Handling - try/catch blocks in place
  5. โœ… Input Validation - Parameter validation attributes used
  6. โœ… No Aliases - Full cmdlet names in scripts
  7. โœ… Path Handling - Use Join-Path or [IO.Path]::Combine()
  8. โœ… Encoding Specified - UTF-8 for cross-platform
  9. โœ… Credentials Secure - Never hardcoded
  10. โœ… Verbose Logging - Write-Verbose for debugging

๐Ÿšจ Common Pitfalls & Solutions

Pitfall: Out-GridView Search Broken in 7.5

# Known Issue: Out-GridView search doesn't work in PowerShell 7.5 due to .NET 9 changes
# Workaround: Use Where-Object or Select-Object for filtering
Get-Process | Where-Object CPU -gt 100 | Format-Table

# Or export to CSV and use external tools
Get-Process | Export-Csv processes.csv -NoTypeInformation

Pitfall: Case Sensitivity

# Linux/macOS are case-sensitive
# This fails on Linux if file is "File.txt"
Get-Content "file.txt"

# Solution: Use exact casing or Test-Path first
if (Test-Path "file.txt") {
    Get-Content "file.txt"
}

Pitfall: Execution Policy

# Solution: Set for current user
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

# Or bypass for session
powershell.exe -ExecutionPolicy Bypass -File script.ps1

Pitfall: Module Import Failures

# Solution: Check module availability and install
if (-not (Get-Module -ListAvailable -Name Az)) {
    Install-Module -Name Az -Force -Scope CurrentUser
}
Import-Module -Name Az

Pitfall: Array Concatenation Performance

# Bad: $array += $item (recreates array each time)

# Good: Use ArrayList or List
$list = [System.Collections.Generic.List[object]]::new()
$list.Add($item)

Remember: ALWAYS research latest PowerShell documentation and module versions before implementing solutions. The PowerShell ecosystem evolves rapidly, and best practices are updated frequently.

Weekly Installs
47
First Seen
Jan 19, 2026
Installed on
claude-code32
opencode31
gemini-cli30
antigravity26
codex23
cursor21