cheat sheet

PowerShell Modules

Install, discover, update, and author PowerShell modules: PowerShellGet, PSResourceGet, the Gallery, $PSModulePath, $PROFILE customisation, PSReadLine, SecretManagement, and the must-have module shortlist.

PowerShell Modules — Packages, Profiles, and the Gallery

What it is

A PowerShell module is the unit of distribution for cmdlets, functions, providers, DSC resources, and formatters — the closest analog to a Python package or an npm module. Modules live on disk under a folder structure that matches their name, are discovered via the $PSModulePath environment variable, and are installed from the PowerShell Gallery (https://www.powershellgallery.com) with Install-Module (classic) or Install-PSResource (modern, PowerShell 7.2+). This article covers discovery and installation, the four $PROFILE variants and which one to edit, the modern Microsoft.PowerShell.PSResourceGet provider, PSReadLine setup, secret management, and the shortlist of modules every PowerShell user should know about.

Module discovery — Find-Module and Find-PSResource

Find-Module searches the configured PowerShell repositories (the Gallery by default) for modules whose name, description, or tags match your query. Find-PSResource is the modern equivalent that ships with Microsoft.PowerShell.PSResourceGet (the rewrite of PowerShellGet v3) — it's faster, doesn't require NuGet, and is the recommended tool going forward.

powershell
# Search by name (wildcards allowed)
Find-Module -Name *Azure*

# Search by tag
Find-Module -Tag 'SQL'

# Pin to a specific version range
Find-Module -Name Az -MinimumVersion 10.0 -MaximumVersion 11.0

# Sort newest first by recent updates
Find-Module -Name *posh* | Sort-Object PublishedDate -Descending |
  Select-Object Name, Version, PublishedDate, Author

Output:

text
Name           Version PublishedDate         Author
----           ------- -------------         ------
posh-git       1.1.0   3/10/2024 12:08:22 AM dahlbyk
posh-sshell    0.5.0   1/22/2024 04:42:11 PM dahlbyk
oh-my-posh.psm 1.4.0   8/14/2023 03:18:55 PM JanDeDobbeleer
powershell
# Modern equivalent — Microsoft.PowerShell.PSResourceGet (PS 7.2+)
Find-PSResource -Name *Azure*
Find-PSResource -Type Module -Tag 'SQL'
Find-PSResource -Name Az -Version '[10.0,11.0)'   # NuGet version range

Output:

text
Name             Version  Prerelease Repository Description
----             -------  ---------- ---------- -----------
Az               10.4.1              PSGallery  Microsoft Azure PowerShell - Cmdlets
Az.Accounts      2.13.1              PSGallery  Microsoft Azure PowerShell - Accounts credential management cmdlets
…

Get-PSRepository lists the repositories you can query; PSGallery is the only one configured by default.

powershell
Get-PSRepository
Get-PSResourceRepository   # modern equivalent

Output:

text
Name      InstallationPolicy SourceLocation
----      ------------------ --------------
PSGallery Untrusted          https://www.powershellgallery.com/api/v2

Installing modules

Install-Module downloads a module from a repository and unpacks it under one of the directories on $PSModulePath. Two flags matter every time: -Scope controls where it lands (CurrentUser = user-only, no admin needed; AllUsers = system-wide, requires admin), and -Force permits overwriting an existing version.

powershell
# Recommended default — user scope, no admin needed
Install-Module Az -Scope CurrentUser

# System-wide — requires elevated session
Install-Module SqlServer -Scope AllUsers

# Install a specific version
Install-Module Pester -RequiredVersion 5.6.1 -Scope CurrentUser

# Allow side-by-side install when the module already exists
Install-Module Az -AllowClobber -Force

# Modern equivalent — same options, slightly different flags
Install-PSResource Az -Scope CurrentUser
Install-PSResource Pester -Version '5.6.1'

Output: (none — installs silently; add -Verbose to see download progress)

By default PSGallery is marked Untrusted, so Install-Module prompts you on every install. To silence the prompt, flip the repository to Trusted (one-time, per scope). Treat this like adding a package source — if you're on a managed machine, ask whether you should pin a private feed instead.

powershell
# One-time: trust the public Gallery
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted

# Modern equivalent
Set-PSResourceRepository -Name PSGallery -Trusted

# Verify
Get-PSRepository PSGallery

Output:

text
Name      InstallationPolicy SourceLocation
----      ------------------ --------------
PSGallery Trusted            https://www.powershellgallery.com/api/v2

Updating and uninstalling

Update-Module upgrades to the latest published version that satisfies any version constraints; if the new version installs side-by-side, both stick around until you remove the old one with Uninstall-Module.

powershell
Update-Module Az                   # update to newest version
Update-Module Az -Force            # overwrite even if running
Update-Module -AcceptLicense       # auto-accept EULAs

# Remove old versions after a successful upgrade
Get-Module Az -ListAvailable | Sort-Object Version |
  Select-Object -SkipLast 1 |
  ForEach-Object { Uninstall-Module $_.Name -RequiredVersion $_.Version -Force }

# Modern equivalents
Update-PSResource Az
Uninstall-PSResource Az -Version '10.0.0'

Output: (none — Update-Module is silent on success; Uninstall-Module emits nothing)

PowerShellGet vs PSResourceGet — which to use

PowerShellGet v2 (the classic) and Microsoft.PowerShell.PSResourceGet (v3, renamed) both ship in modern PowerShell, expose overlapping cmdlets, and talk to the same Gallery. Prefer PSResourceGet (Install-PSResource, Find-PSResource, …) on PowerShell 7.2 and later: it's an order of magnitude faster, removes the NuGet dependency, and supports proper SemVer ranges. For scripts that have to run on Windows PowerShell 5.1, stay on the classic cmdlets — PSResourceGet is PowerShell 7-only. PowerShell 7.6 LTS (March 2026) ships both modules side-by-side, and the ThreadJob module was officially renamed to Microsoft.PowerShell.ThreadJob in that release.

powershell
# Check which versions you have
Get-Module PowerShellGet, Microsoft.PowerShell.PSResourceGet -ListAvailable |
  Select-Object Name, Version

Output:

text
Name                                Version
----                                -------
Microsoft.PowerShell.PSResourceGet  1.0.5
PowerShellGet                       2.2.5
PowerShellGet                       1.0.0.1

$PSModulePath — where modules live

$PSModulePath is a ;-separated (Windows) or :-separated (Linux/macOS) list of directories that PowerShell scans during module auto-loading. Out of the box it contains three locations on Windows: the user scope, the all-users scope, and the system scope shipped with the PowerShell install. Auto-loading means Get-Process works without an explicit Import-Module — the engine finds Microsoft.PowerShell.Management on $PSModulePath the first time you reference a command from it.

powershell
$env:PSModulePath -split [System.IO.Path]::PathSeparator

Output (Windows, PowerShell 7):

text
C:\Users\Alice\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules
PathScopeWho can write
C:\Users\<user>\Documents\PowerShell\ModulesCurrentUser (PS 7)The user
C:\Users\<user>\Documents\WindowsPowerShell\ModulesCurrentUser (PS 5.1)The user
C:\Program Files\PowerShell\ModulesAllUsers (PS 7)Admin
C:\Program Files\PowerShell\7\ModulesShipped-with-PS-7(don't edit)
C:\Program Files\WindowsPowerShell\ModulesAllUsers (PS 5.1)Admin

To add an extra location — say a network share with team-internal modules — append to $env:PSModulePath in your profile:

powershell
$shared = '\\myserver\share\PowerShell\Modules'
if ($env:PSModulePath -notlike "*$shared*") {
  $env:PSModulePath = "$shared$([System.IO.Path]::PathSeparator)$env:PSModulePath"
}

Output: (none — silently mutates $env:PSModulePath for the current session)

Importing and unloading

PowerShell auto-imports a module the first time you reference a command it exports. You only need Import-Module explicitly when you want to load a path-based module, force a fresh load, or import with a custom prefix.

powershell
# Auto-load happens automatically
Get-AzAccessToken                 # triggers Import-Module Az.Accounts

# Explicit import — by name
Import-Module Az.Accounts

# Explicit import — by absolute path (no $PSModulePath required)
Import-Module C:\Users\Alice\modules\MyCustomTools\MyCustomTools.psd1

# Force reload — useful after editing a module mid-session
Import-Module MyCustomTools -Force

# Import with a prefix to avoid name collisions
Import-Module SqlServer -Prefix Sql
# Now: Get-SqlSqlDatabase, Invoke-SqlSqlcmd, etc.

# Inspect what's currently loaded
Get-Module

# Inspect what's available on disk but not loaded
Get-Module -ListAvailable
Get-Module -ListAvailable Az.* | Select-Object Name, Version

# Unload
Remove-Module Az.Accounts

Output (Get-Module):

text
ModuleType Version    Name                            ExportedCommands
---------- -------    ----                            ----------------
Script     2.3.4      PSReadLine                      {Get-PSReadLineKeyHandler, Get-PSReadLineOption, …}
Manifest   7.0.0.0    Microsoft.PowerShell.Management {Add-Content, Clear-Content, Clear-Item, …}
Manifest   7.0.0.0    Microsoft.PowerShell.Utility    {Add-Member, Add-Type, Clear-Variable, …}

$PROFILE — the four startup scripts

$PROFILE is a variable that points to the profile script for the current user and host — but it's actually one of four scripts that all run on shell startup, in a documented order. Editing the right one is the difference between "my new alias works in pwsh.exe but not in VS Code's terminal" and "it works everywhere". $PROFILE itself is Microsoft.PowerShell_profile.ps1 in your Documents\PowerShell folder — that's the CurrentUser/CurrentHost variant.

powershell
# Inspect all four
$PROFILE | Format-List * -Force

Output:

text
AllUsersAllHosts       : C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost    : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts    : C:\Users\Alice\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\Alice\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Length                 : 67
VariantPath (PS 7)Edit for
AllUsersAllHosts$PSHOME\profile.ps1System-wide setup affecting every PS host (requires admin)
AllUsersCurrentHost$PSHOME\Microsoft.PowerShell_profile.ps1System-wide setup for pwsh only
CurrentUserAllHosts~\Documents\PowerShell\profile.ps1Your personal setup, every host (pwsh + ISE + VS Code terminal + Azure Cloud Shell)
CurrentUserCurrentHost~\Documents\PowerShell\Microsoft.PowerShell_profile.ps1Your personal setup, pwsh only (this is $PROFILE)

Pick CurrentUserAllHosts for almost everything — it loads in every PowerShell host you use, so VS Code, Windows Terminal, and the ISE all share the same aliases and key bindings. Reserve the CurrentUserCurrentHost script for things that genuinely shouldn't run in non-interactive hosts.

powershell
# Create the file if it doesn't exist, then open it
if (-not (Test-Path $PROFILE.CurrentUserAllHosts)) {
  New-Item -ItemType File -Path $PROFILE.CurrentUserAllHosts -Force
}
code $PROFILE.CurrentUserAllHosts   # or:  notepad $PROFILE.CurrentUserAllHosts

Output: (none — opens the editor)

A starter profile:

powershell
# ~\Documents\PowerShell\profile.ps1

# Better history search and predictions
Import-Module PSReadLine
Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Key UpArrow   -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward
Set-PSReadLineOption -HistoryNoDuplicates -HistorySearchCursorMovesToEnd

# Sensible aliases (only set ones you actually want to type)
Set-Alias -Name ll  -Value Get-ChildItem
Set-Alias -Name g   -Value git
function .. { Set-Location .. }
function ... { Set-Location ..\.. }

# Prompt — show pwd and last exit code
function prompt {
  $ec = if ($?) { 'OK ' } else { 'ERR ' }
  $cwd = $PWD.Path.Replace($HOME, '~')
  "[$ec$cwd] PS> "
}

# Helpful per-machine welcome
"$env:USERNAME on $env:COMPUTERNAME — PS $($PSVersionTable.PSVersion)"

Output (on next session start):

text
Alice on MYHOST — PS 7.4.3
[OK ~] PS>

PSReadLine — line editor superpowers

PSReadLine ships with PowerShell 5.1+ and is the line editor responsible for syntax colouring, history search, prediction, and key bindings. It's a module like any other — updates ship via the Gallery. Upgrading PSReadLine is one of the single highest-ROI improvements you can make to your shell.

powershell
# Update PSReadLine to latest (closes a few long-standing bugs in 7's bundled version)
Install-Module PSReadLine -Force -SkipPublisherCheck -Scope CurrentUser

# Current version and where it loaded from
Get-Module PSReadLine | Select-Object Name, Version, Path

Output:

text
Name       Version Path
----       ------- ----
PSReadLine 2.3.4   C:\Users\Alice\Documents\PowerShell\Modules\PSReadLine\2.3.4\PSReadLine.psd1
powershell
# Inspect the current options
Get-PSReadLineOption | Format-List Edit*, Predict*, History*

# Enable inline predictions powered by history (plus plugins like Az)
Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Set-PSReadLineOption -PredictionViewStyle ListView    # multiline picker

# Make Tab cycle through completion matches
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete

# Up-arrow searches history with the current prefix
Set-PSReadLineKeyHandler -Key UpArrow   -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward

# Save more history and dedupe
Set-PSReadLineOption -MaximumHistoryCount 5000 -HistoryNoDuplicates

Output (Get-PSReadLineOption):

text
EditMode                              : Windows
PredictionSource                      : HistoryAndPlugin
PredictionViewStyle                   : ListView
HistorySaveStyle                      : SaveIncrementally
HistorySearchCursorMovesToEnd         : True
MaximumHistoryCount                   : 5000
HistoryNoDuplicates                   : True

After this, hitting Up after typing git c shows just past commands starting with git c, and grey-text predictions appear as you type — accept with RightArrow or End.

SecretManagement — credentials without plaintext

Microsoft.PowerShell.SecretManagement (the module) plus a vault extension (e.g. Microsoft.PowerShell.SecretStore, SecretManagement.KeePass, SecretManagement.LastPass) gives you a unified Get-Secret / Set-Secret API that never writes plaintext credentials to disk. Scripts call Get-Secret 'name' and the right vault prompts (once per session) or unlocks automatically based on its policy.

powershell
# Install the module and a default local-disk vault
Install-Module Microsoft.PowerShell.SecretManagement -Scope CurrentUser
Install-Module Microsoft.PowerShell.SecretStore       -Scope CurrentUser

# Register the local vault as the default (one-time setup)
Register-SecretVault -Name 'LocalVault' -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault

# Set a password for the vault on first use (you can also disable the prompt)
Set-SecretStoreConfiguration -Authentication Password -PasswordTimeout 900 -Interaction Prompt

# Store a credential — interactive prompt for the secret
Set-Secret -Name 'github-pat' -Secret (Read-Host -AsSecureString)

# Store a PSCredential object
Set-Secret -Name 'svc-deploy' -Secret (Get-Credential -UserName 'alice@example.com')

# Retrieve
$pat = Get-Secret -Name 'github-pat' -AsPlainText
$cred = Get-Secret -Name 'svc-deploy'

# Enumerate
Get-SecretInfo

Output (Get-SecretInfo):

text
Name        Type         VaultName
----        ----         ---------
github-pat  String       LocalVault
svc-deploy  PSCredential LocalVault

For Azure Key Vault, swap the extension: Install-Module Az.KeyVault; Register-SecretVault -Name AzKV -ModuleName Az.KeyVault -VaultParameters @{ AZKVaultName = 'corp-kv' }. The Get-Secret/Set-Secret API stays the same — your scripts don't change.

Module manifests — the .psd1 file

A module manifest is a .psd1 file (PowerShell data file — restricted-language hash table) that describes a module: its version, exports, dependencies, license, and PowerShell version requirements. The Gallery uses manifest metadata for the listing page; Import-Module uses it to load the right files and validate requirements. Run New-ModuleManifest to scaffold one with sensible defaults.

powershell
# Create a manifest for a new module
$manifest = @{
  Path             = 'C:\Users\Alice\src\MyTools\MyTools.psd1'
  RootModule       = 'MyTools.psm1'
  ModuleVersion    = '1.0.0'
  Author           = 'Alice Dev'
  CompanyName      = 'Example Co'
  Description      = 'Internal automation cmdlets.'
  PowerShellVersion= '7.2'
  FunctionsToExport= @('Get-Widget', 'Set-Widget')
  CmdletsToExport  = @()
  AliasesToExport  = @()
  Tags             = @('internal', 'automation')
  ProjectUri       = 'https://example.com/mytools'
  LicenseUri       = 'https://example.com/mytools/LICENSE'
}
New-ModuleManifest @manifest

# Inspect a manifest non-destructively
Test-ModuleManifest C:\Users\Alice\src\MyTools\MyTools.psd1

Output (Test-ModuleManifest):

text
ModuleType Version    PreRelease Name      ExportedCommands
---------- -------    ---------- ----      ----------------
Script     1.0.0                 MyTools   {Get-Widget, Set-Widget}

For a one-file module (just a .psm1), you can skip the manifest, but you forfeit Gallery publishing and most discoverability features. Always create a manifest for any module that will be reused.

The modules below are widely used, actively maintained, and worth installing on day one of a new machine. Each row notes what it gives you, the canonical install command, and whether it works on PowerShell 5.1 too.

ModuleWhat it doesInstallPS 5.1?
PSReadLineLine editor — history, predictions, syntax colourInstall-Module PSReadLine -Forceyes
Microsoft.PowerShell.SecretManagementUnified secret-store APIInstall-Module Microsoft.PowerShell.SecretManagementyes
Microsoft.PowerShell.SecretStoreLocal encrypted store for the API aboveInstall-Module Microsoft.PowerShell.SecretStoreyes
AzOfficial Azure cmdlets (umbrella module)Install-Module Azyes
Microsoft.GraphMicrosoft Graph (M365, Entra, Intune)Install-Module Microsoft.Graphyes
PesterUnit & integration testing frameworkInstall-Module Pester -Force -SkipPublisherCheckyes
PSScriptAnalyzerStatic analysis (lints scripts)Install-Module PSScriptAnalyzeryes
posh-gitGit-aware prompt segmentInstall-Module posh-gityes
Terminal-IconsFile-icon glyphs in Get-ChildItemInstall-Module Terminal-Iconsyes
ThreadJobLightweight in-process jobs (alt. to Start-Job)Install-Module ThreadJobyes
ImportExcelRead/write .xlsx without Excel installedInstall-Module ImportExcelyes
oh-my-posh (helper)Prompt themes (the CLI install is separate)see project docsyes
powershell
# One-line bootstrap on a new machine
$wanted = @(
  'PSReadLine','Microsoft.PowerShell.SecretManagement',
  'Microsoft.PowerShell.SecretStore','Pester','PSScriptAnalyzer',
  'posh-git','Terminal-Icons','ImportExcel'
)
$wanted | ForEach-Object {
  if (-not (Get-Module $_ -ListAvailable)) {
    Install-Module $_ -Scope CurrentUser -Force -SkipPublisherCheck
  }
}

Output: (per-module — typically none; if a module installs, you'll see download progress with -Verbose)

Common pitfalls

  1. Editing the wrong $PROFILE$PROFILE is CurrentUserCurrentHost. Things you want in VS Code's terminal too belong in $PROFILE.CurrentUserAllHosts.
  2. Installing AllUsers without admin — silently falls back to user scope or errors; always specify -Scope CurrentUser explicitly to avoid the surprise.
  3. Forgetting to trust PSGallery — every install prompts. Set -InstallationPolicy Trusted once, in your provisioning script.
  4. Update-Module leaving old versions behind — PowerShell installs side-by-side. Clean up with Uninstall-Module -RequiredVersion.
  5. Manifest's FunctionsToExport = '*' — exports nothing in PowerShell 7.x if RootModule is a script (.psm1) without explicit Export-ModuleMember calls. Always list functions explicitly.
  6. Adding a path to $PSModulePath and expecting it to persist$env:PSModulePath is process-scoped. Edit your profile, or use [Environment]::SetEnvironmentVariable('PSModulePath', $new, 'User') for a permanent change.
  7. Mixing PowerShellGet v2 cmdlets and PSResourceGet v3 cmdlets on the same module — the two providers don't share state. Pick one per machine.
  8. Importing a path-based module without -Force after edits — the cached version sticks around for the session. Always reload with Import-Module … -Force during development.

Real-world recipes

Bootstrap a new dev machine in one script

This script trusts the Gallery, installs the must-have shortlist, sets PSReadLine options, writes a starter profile, and reports anything that fails — designed to run once on a fresh machine.

powershell
# bootstrap-pwsh.ps1
[CmdletBinding()] param()

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# 1. Trust the Gallery
if ((Get-PSRepository PSGallery).InstallationPolicy -ne 'Trusted') {
  Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
  Write-Host '✓ Gallery trusted'
}

# 2. Install modules
$wanted = @(
  'PSReadLine','Pester','PSScriptAnalyzer','posh-git','Terminal-Icons',
  'Microsoft.PowerShell.SecretManagement','Microsoft.PowerShell.SecretStore',
  'ImportExcel','ThreadJob'
)
foreach ($m in $wanted) {
  if (-not (Get-Module $m -ListAvailable)) {
    Install-Module $m -Scope CurrentUser -Force -SkipPublisherCheck
    Write-Host "✓ installed $m"
  } else {
    Write-Host "- $m already present"
  }
}

# 3. Write profile
$profilePath = $PROFILE.CurrentUserAllHosts
if (-not (Test-Path $profilePath)) {
  New-Item -ItemType File -Path $profilePath -Force | Out-Null
}
$profileBody = @'
Import-Module PSReadLine
Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Key UpArrow   -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward
Set-PSReadLineOption -HistoryNoDuplicates -MaximumHistoryCount 5000
Import-Module Terminal-Icons -ErrorAction SilentlyContinue
Import-Module posh-git       -ErrorAction SilentlyContinue
'@
Set-Content -Path $profilePath -Value $profileBody -Encoding UTF8
Write-Host "✓ profile written to $profilePath"
Write-Host 'Done — start a new pwsh session to pick up the changes.'

Output:

text
✓ Gallery trusted
✓ installed PSReadLine
- Pester already present
✓ installed PSScriptAnalyzer
✓ installed posh-git
✓ installed Terminal-Icons
✓ installed Microsoft.PowerShell.SecretManagement
✓ installed Microsoft.PowerShell.SecretStore
✓ installed ImportExcel
✓ installed ThreadJob
✓ profile written to C:\Users\Alice\Documents\PowerShell\profile.ps1
Done — start a new pwsh session to pick up the changes.

A profile that loads predictive history + sensible aliases

Drop this into $PROFILE.CurrentUserAllHosts for a balanced setup: faster history, completion menu, common aliases, a minimal prompt, and per-session welcome.

powershell
# ~/Documents/PowerShell/profile.ps1

# PSReadLine — predictions, history search, completion menu
Import-Module PSReadLine
Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Key UpArrow   -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward
Set-PSReadLineKeyHandler -Chord 'Ctrl+l' -Function ClearScreen
Set-PSReadLineOption -HistoryNoDuplicates -MaximumHistoryCount 5000 -HistorySearchCursorMovesToEnd

# Terminal candy
Import-Module Terminal-Icons -ErrorAction SilentlyContinue

# Aliases & helper functions
Set-Alias -Name ll  -Value Get-ChildItem
Set-Alias -Name grep -Value Select-String
function .. { Set-Location .. }
function ... { Set-Location ..\.. }
function which { param([string]$cmd) (Get-Command $cmd).Source }

# Quick edit-and-reload of this profile
function Edit-Profile { code $PROFILE.CurrentUserAllHosts }
function Reload-Profile { . $PROFILE.CurrentUserAllHosts; 'profile reloaded' }

# Minimal prompt with exit-code colour cue
function prompt {
  $ok = $?
  $cwd = $PWD.Path.Replace($HOME, '~')
  $colour = if ($ok) { "`e[32m" } else { "`e[31m" }
  "$colour[$cwd]`e[0m PS> "
}

# Welcome banner — only in interactive sessions
if ($Host.UI.RawUI -and -not $env:PROFILE_QUIET) {
  "$env:USERNAME @ $env:COMPUTERNAME — PS $($PSVersionTable.PSVersion)" |
    Write-Host -ForegroundColor DarkGray
}

Output (first prompt after starting pwsh):

text
Alice @ MYHOST — PS 7.4.3
[~] PS> _

Publish a small internal module to a private repository

When you have a private NuGet feed (Azure Artifacts, GitHub Packages, ProGet), register it once and Publish-Module to it on every release. CI pipelines do the same with an API key in an env var.

powershell
# Register the private feed (one-time per machine)
Register-PSRepository -Name 'CorpInternal' `
  -SourceLocation 'https://nuget.example.com/v3/index.json' `
  -PublishLocation 'https://nuget.example.com/v3/index.json' `
  -InstallationPolicy Trusted

# Publish from the module folder
Publish-Module -Path 'C:\Users\Alice\src\MyTools' `
  -Repository 'CorpInternal' `
  -NuGetApiKey $env:CORP_NUGET_KEY

# Users on other machines install with -Repository
Install-Module MyTools -Repository CorpInternal -Scope CurrentUser

Output: (none on success — Publish-Module prints diagnostic info if -Verbose is set)

Audit which modules are out of date

A maintenance one-liner that compares each installed module against the Gallery's latest version and prints anything behind by at least one patch.

powershell
Get-InstalledModule |
  ForEach-Object {
    $latest = (Find-Module $_.Name -ErrorAction SilentlyContinue).Version
    if ($latest -and $latest -gt $_.Version) {
      [pscustomobject]@{
        Name      = $_.Name
        Installed = $_.Version
        Latest    = $latest
        Update    = "Update-Module $($_.Name)"
      }
    }
  } |
  Format-Table -AutoSize

Output:

text
Name         Installed Latest Update
----         --------- ------ ------
Pester       5.4.0     5.6.1  Update-Module Pester
posh-git     1.0.0     1.1.0  Update-Module posh-git
PSReadLine   2.2.6     2.3.4  Update-Module PSReadLine

Use SecretManagement to feed credentials into a deploy script

A deployment script asks for a stored credential — the user never types a password and the credential never lands in plaintext on disk.

powershell
function Invoke-DeployToProd {
  [CmdletBinding()]
  param([Parameter(Mandatory)] [string]$ReleaseTag)

  $cred = Get-Secret -Name 'svc-deploy' -ErrorAction Stop
  $token = Get-Secret -Name 'github-pat' -AsPlainText -ErrorAction Stop

  Write-Verbose "Deploying $ReleaseTag as $($cred.UserName)"

  # ... use $cred and $token ...
  [pscustomobject]@{
    Tag       = $ReleaseTag
    Operator  = $cred.UserName
    Started   = Get-Date
    Status    = 'Submitted'
  }
}

Invoke-DeployToProd -ReleaseTag 'v2.4.1' -Verbose

Output:

text
VERBOSE: Deploying v2.4.1 as alice@example.com

Tag    Operator          Started               Status
---    --------          -------               ------
v2.4.1 alice@example.com 5/26/2026 9:14:02 AM  Submitted

Sources