Skip to content

Latest commit

 

History

History
804 lines (527 loc) · 17.3 KB

best-practices.md

File metadata and controls

804 lines (527 loc) · 17.3 KB

PowerShell IT Support Toolkit — Best Practices

1. Purpose

This document defines the recommended practices for using, maintaining, and extending the PowerShell IT Support Toolkit.

The goal is to ensure that toolkit scripts remain:

  • Reliable
  • Safe to execute
  • Consistent with the project architecture
  • Maintainable
  • Predictable for IT support operations
  • Easy to troubleshoot and extend
  • Suitable for professional IT support environments

These guidelines apply to anyone who uses, reviews, maintains, or contributes to the toolkit.


2. Core Principles

All toolkit development and usage should follow these principles:

2.1 Safety First

Scripts must prioritize system safety over convenience.

A script should never:

  • Delete or modify system data without explicit justification.
  • Execute destructive operations without appropriate validation.
  • Modify configuration outside its documented scope.
  • Trust user-provided paths or values without validation.
  • Disable security controls unnecessarily.
  • Use elevated privileges when they are not required.

When an operation can fail safely, it should fail safely.

2.2 Predictable Behavior

Each script should have a clearly defined responsibility.

A script should:

  • Perform the operation described by its name.
  • Preserve documented parameters.
  • Produce predictable output.
  • Handle expected failures gracefully.
  • Avoid unexpected side effects.

Avoid combining unrelated administrative tasks into a single script.

2.3 Consistency

Scripts should follow the same conventions throughout the toolkit.

This includes:

  • Approved PowerShell verbs
  • Consistent parameter naming
  • Common module usage
  • Error-handling conventions
  • Reporting conventions
  • Output formatting
  • Path handling
  • Documentation style

Consistency makes the toolkit easier to operate and maintain.

2.4 Minimal Changes

When maintaining an existing script:

Fix the actual problem without unnecessarily changing correct behavior.

Do not refactor working code simply because another implementation appears cleaner.

Any modification should have a clear technical reason.


3. PowerShell Coding Standards

3.1 Use Approved PowerShell Verbs

Functions and scripts should use approved PowerShell verbs whenever possible.

Examples:

Purpose Recommended Verb
Retrieve information Get-
Create something New-
Remove something Remove-
Update something Set-
Invoke an operation Invoke-
Test a condition Test-
Export data Export-
Import data Import-
Start an operation Start-
Stop an operation Stop-

Examples:

Get-SystemInfo
Get-Processes
Get-AllServices
Invoke-NetworkDiagnostics
Clear-TempFiles
Export-EventLogs
New-PCReport

Avoid inventing non-standard verbs when an approved PowerShell verb accurately describes the operation.


3.2 Use Clear Names

Names should communicate purpose without requiring the reader to inspect the implementation.

Prefer:

$CommonModule
$ScriptsRoot
$ExportPath
$TargetPath
$FilesDeleted

Avoid unnecessarily cryptic names such as:

$x
$tmp
$obj1
$data2

Short variables are acceptable for very small local scopes when their meaning is obvious.


3.3 Use Parameters for User-Controlled Input

Values that users may reasonably need to change should generally be exposed through parameters rather than hardcoded.

Example:

param(
    [string]$ExportPath
)

Parameters should have:

  • Clear names
  • Appropriate types
  • Validation where necessary
  • Useful defaults when appropriate
  • Documentation in the comment-based help

Do not add parameters simply to increase flexibility.


3.4 Validate Input

Input validation is particularly important for scripts that:

  • Delete files
  • Modify configuration
  • Create users
  • Change networking settings
  • Export data
  • Interact with system paths

Validation should reject unsafe or invalid input before the destructive operation begins.

For example:

[ValidateRange(0, 3650)]
[int]$OlderThanDays = 0

Validation should be proportional to the risk of the operation.


4. Error Handling

4.1 Use Strict Mode

Toolkit scripts should use:

Set-StrictMode -Version Latest

This helps identify:

  • Undefined variables
  • Invalid property access
  • Other programming mistakes

Strict mode should be combined with deliberate error handling rather than disabled to hide implementation problems.


4.2 Use a Consistent Error Preference

Toolkit scripts generally use:

$ErrorActionPreference = 'Stop'

This allows unexpected errors to reach the script's established error-handling path.

Individual commands may override this behavior when a failure is expected and intentionally handled.

For example:

Get-Process -ErrorAction SilentlyContinue

should only be used when the script has a deliberate reason to tolerate command-level failures.

Do not use SilentlyContinue simply to hide errors.


4.3 Preserve the Project Error Pattern

Scripts should use the established project pattern:

try {
    # Main operation
}
catch {
    Write-ErrorMessage ("[{0}] {1}" -f $_.Exception.GetType().Name, $_.Exception.Message)
    exit 1
}

This provides consistent error reporting across the toolkit.

Do not introduce a completely different error-handling architecture for an individual script without a strong technical reason.


4.4 Handle Expected Failures Locally

Some operations are naturally unreliable.

Examples include:

  • Reading information from protected processes
  • Accessing locked files
  • Querying unavailable services
  • Reading restricted event logs
  • Accessing network resources

These failures should be handled at the smallest appropriate scope.

For example, if one process property cannot be read, the entire process-report operation should not necessarily fail.

Prefer:

try {
    $description = $process.Description
}
catch {
    $description = $null
}

when an unavailable description is an expected condition.

Do not catch errors without deciding what the failure means.


5. Security and Safe Script Execution

5.1 Principle of Least Privilege

Scripts should operate with the minimum privileges required.

Do not require Administrator privileges simply because they are convenient.

Administrator privileges should only be required when the underlying Windows operation genuinely requires them.


5.2 Never Disable Security Controls Unnecessarily

Do not introduce mechanisms such as:

-ExecutionPolicy Bypass

unless there is a clearly documented and justified requirement.

The toolkit should not weaken the host security configuration merely to make script execution easier.


5.3 Avoid Unsafe Command Construction

Do not construct executable commands from untrusted input.

Avoid patterns where user input is directly inserted into dynamically executed code or command strings.

Prefer PowerShell parameters and the call operator:

& $ExecutablePath -ArgumentList $Arguments

over dynamically constructed command text.


5.4 Validate File-System Paths

Scripts that operate on paths should:

  1. Resolve the intended path.
  2. Verify that the path exists when required.
  3. Verify whether it is a file or directory.
  4. Validate that it is within the intended scope.
  5. Reject dangerous system locations when the operation is destructive.

Destructive scripts should never assume that a user-provided path is safe.


5.5 Never Delete the Target Directory Accidentally

Cleanup operations should delete only the intended contents.

The target directory itself should remain intact unless deleting the directory is explicitly the documented purpose of the script.

This is especially important for:

  • Temporary-file cleanup
  • Cache cleanup
  • Log cleanup
  • Backup cleanup

5.6 Protect Against Path Scope Errors

Before deleting or modifying files recursively, verify that the resolved path is not an unintended parent directory.

Dangerous targets include locations such as:

  • A filesystem root
  • Windows system directories
  • Program installation directories
  • User profile roots
  • Other critical application data directories

Safety checks should be based on resolved paths rather than only the raw user input.


6. File-System Operations

6.1 Use Literal Paths When Appropriate

When working with paths supplied as literal file-system paths, prefer:

-LiteralPath

when wildcard expansion is not intended.

Example:

Get-Item -LiteralPath $Path

This prevents special wildcard characters from unexpectedly changing the target.


6.2 Do Not Follow Unintended Targets

Recursive operations should remain within the intended scope.

Be particularly careful with:

  • Reparse points
  • Symbolic links
  • Junctions
  • Mounted locations

A cleanup script should never unexpectedly traverse into another volume or unrelated directory tree.


6.3 Track Operation Results Accurately

Counters and reports should reflect what actually happened.

For example:

FilesScanned
FilesDeleted
FilesFailed
BytesFreed

BytesFreed must only include files that were successfully deleted.

Do not increment success counters before the operation actually succeeds.


7. Reporting and Output

7.1 Preserve Established Report Structures

Scripts should use the project's established reporting mechanisms where applicable.

For example:

Export-ToolkitReport

should be preferred over creating a separate export implementation.

This keeps report formats consistent across the toolkit.


7.2 Use Structured Objects for Data

When producing report data, prefer:

[PSCustomObject]@{
    Id       = $process.Id
    Name     = $process.Name
    CPU      = $cpu
    MemoryMB = $memory
}

Structured objects are easier to:

  • Export
  • Filter
  • Test
  • Consume programmatically
  • Convert to JSON

7.3 Console Output Should Remain Human-Friendly

Use the project's common output functions when available:

Write-Info
Write-Success
Write-WarningMessage
Write-ErrorMessage
Write-Section
Show-SystemReport

Avoid implementing a new output style inside an individual script without a technical reason.


8. Common Module Usage

The toolkit uses shared modules to avoid duplicating common functionality.

Before creating a new helper function, check whether the required functionality already exists in:

scripts\common\

Relevant shared functionality may include:

  • Common output
  • Logging
  • Reporting
  • Path handling
  • Utility functions

Do not duplicate existing functionality inside individual scripts unless there is a clear reason.


8.1 Do Not Modify Common Modules Casually

Changes to:

scripts\common\Common.psm1
scripts\common\Logger.psm1
scripts\common\Utilities.psm1

can affect many scripts.

Before changing a shared module:

  1. Identify every script that depends on it.
  2. Verify the intended behavior.
  3. Determine whether the change is backward-compatible.
  4. Test affected scripts.
  5. Avoid changing public behavior unnecessarily.

9. Toolkit Architecture and Maintainability

9.1 Keep Scripts Single-Purpose

Each script should have one primary responsibility.

For example:

Get-SystemInfo.ps1

should collect system information rather than also modifying network settings or cleaning temporary files.

This improves:

  • Testing
  • Reusability
  • Troubleshooting
  • Maintainability

9.2 Keep the Orchestrator Lightweight

IT-Toolkit.ps1 acts as the interactive entry point.

Its primary responsibilities should remain:

  • Displaying the menu
  • Accepting user input
  • Selecting the appropriate backend script
  • Handling high-level execution status

Business or administrative logic should remain in the specialized scripts.


9.3 Use $PSScriptRoot for Project Paths

Project-relative paths should be constructed from $PSScriptRoot.

Example:

$CommonModule = Join-Path $PSScriptRoot "..\common\Common.psm1"

Avoid assuming that the user launched the script from a specific working directory.

This allows scripts to work correctly when launched from:

  • PowerShell
  • The toolkit orchestrator
  • Another working directory
  • Automation environments

9.4 Avoid Unnecessary Dependencies

Only import modules that the script actually requires.

Unused imports:

  • Increase coupling
  • Make troubleshooting harder
  • Increase startup complexity
  • Can create unnecessary failure points

Before adding a module dependency, verify that the script actually uses functionality provided by that module.


9.5 Preserve Public Interfaces

When maintaining an existing script, treat these as part of its public interface:

  • Script name
  • Parameters
  • Parameter defaults
  • Expected output
  • Report fields
  • Export behavior
  • Exit behavior
  • Menu integration

Do not change these unless a genuine defect requires it.


10. Script Documentation Standards

Each toolkit script should contain useful comment-based help.

At minimum, document:

.SYNOPSIS
.DESCRIPTION
.PARAMETER
.EXAMPLE
.NOTES

Documentation should explain what the script does rather than merely restating its filename.

Examples should be realistic and executable.


11. Testing and Validation

Every modified script should be validated before being considered complete.

11.1 Syntax Validation

Use PowerShell's parser to verify syntax.

Example:

$errors = $null

[System.Management.Automation.Language.Parser]::ParseFile(
    $ScriptPath,
    [ref]$null,
    [ref]$errors
)

if ($errors) {
    Write-Host "SYNTAX: FAILED"
}
else {
    Write-Host "SYNTAX: OK"
}

11.2 Functional Testing

Test the normal execution path.

Verify:

  • Expected output
  • Expected report structure
  • Correct exit behavior
  • Correct file operations
  • Correct parameter handling

11.3 Failure Testing

Do not test only successful scenarios.

Where applicable, test:

  • Invalid paths
  • Missing files
  • Access denied
  • Locked files
  • Protected processes
  • Invalid parameters
  • Missing dependencies
  • Failed child scripts
  • Export failures

A script is not considered reliable simply because the happy path works.


11.4 Test Through the Toolkit

When a script is exposed through IT-Toolkit.ps1, test it through the actual menu as well as directly.

This verifies:

  • Backend mapping
  • Child-process execution
  • Exit-code handling
  • Menu recovery
  • Logging behavior

12. Change Management

Before modifying an existing script:

  1. Read the entire script.
  2. Inspect the modules it depends on.
  3. Identify the actual defect.
  4. Confirm that the behavior is genuinely incorrect.
  5. Make the smallest safe change.
  6. Re-run syntax validation.
  7. Test the affected functionality.
  8. Verify that unrelated functionality remains unchanged.

Do not make changes merely because an alternative implementation looks cleaner.


13. Security Review Checklist

Before considering a security-sensitive script complete, verify:

  • User input is validated.
  • File-system paths are resolved safely.
  • Destructive operations have safety checks.
  • Administrator privileges are only used when necessary.
  • Security controls are not unnecessarily disabled.
  • Dynamic command execution is avoided where possible.
  • Recursive operations cannot unexpectedly escape their intended scope.
  • Errors do not result in misleading success messages.
  • Sensitive information is not unnecessarily written to logs.
  • Temporary test artifacts are removed after validation.

14. IT Support Operational Checklist

Before using a toolkit script during an IT support task:

  • Confirm the intended target system.
  • Understand what the script will change or collect.
  • Review parameters before execution.
  • Confirm that administrative privileges are appropriate.
  • Avoid destructive operations unless required.
  • Preserve relevant reports and diagnostic output.
  • Record important failures or warnings.
  • Verify the result after the operation.
  • Escalate when the operation is outside the script's documented scope.

15. Maintenance Checklist

When adding or modifying a toolkit script:

  • Follow the existing project architecture.
  • Use approved PowerShell verbs.
  • Add comment-based help.
  • Use appropriate parameters.
  • Validate risky input.
  • Use Set-StrictMode -Version Latest.
  • Follow the project's error-handling pattern.
  • Reuse common modules where appropriate.
  • Avoid unnecessary dependencies.
  • Preserve existing report structures.
  • Test both success and failure paths.
  • Validate integration with IT-Toolkit.ps1 when applicable.
  • Do not modify unrelated files.
  • Verify the final files after editing.

16. Golden Rule

The most important maintenance principle for this toolkit is:

Find the real problem → fix the real problem → verify the fix → preserve everything else.

A successful change is not the change that introduces the most improvements.

A successful change is the smallest justified change that makes the toolkit safer, more reliable, or more correct without breaking established behavior.