Things I've learned building this repository.
- Default Powershell $PROFILE path
- Check Powershell version
- Automatic updates to module's manifest
- Generate profile GUID
- Pass -Verbose and -Debug to other scripts
- Dynamically set aliases
Current User, Current Host:
C:\Users\$USERNAME\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
Current User, All Hosts:
C:\Users\$USERNAME\Documents\WindowsPowerShell\profile.ps1
All Users, Current Host:
%WINDIR%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
All Users, All Hosts:
%WINDIR%\System32\WindowsPowerShell\v1.0\profile.ps1
Current User, Current Host:
C:\Users\$USERNAME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Current User, All Hosts:
C:\Users\$USERNAME\Documents\PowerShell\profile.ps1
All Users, Current Host:
C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
All Users, All Hosts:
C:\Program Files\PowerShell\7\profile.ps1
~/.config/powershell/Microsoft.PowerShell_profile.ps1
You can use the $PSVersionTable.PSVersion variable to get the current shell's version. Below are examples of what you will see when you run this command:
Powershell 5:
$ $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 26100 2161Powershell 7:
$PSVersionTable.PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
7 5 0You can also select only the major/minor version:
## Select the major version
$PSVersionTable.PSVersion.Major.ToString()
## Select the minor version:
$PSVersionTable.PSVersion.Minor.ToString()Or get the whole version as a string:
$PSVersionTable.PSVersion.ToString()You can use the Powershell version's major (or major/minor) release string in a conditional statement. To write a conditional check using the shell's version, use:
## Store Powershell major and minor versions in variables
$PowershellMajorVersion = $PSVersionTable.PSVersion.Major.ToString()
$PowershellMinorVersion = $PSVersionTable.PSVersion.Minor.ToString()
## Check if Powershell major version is 7
if ( $PowershellMajorVersion -eq "7" ) {
## Do something when shell is Powershell 7/Core
...
} elseif ( $PowershellMajorVersion -eq "5" ) {
## Do something when shell is Powershell 5
...
}An example of when you might want to conditionally check the shell's version is when setting TLS with [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12. This step is unnecessary on Powershell 7, and will slow the shell down. To ensure this command only runs on Powershell 5:
## Set TLS to 1.2 on Powershell 5 prompts
if ($PSVersionTable.PSVersion.Major -eq 5) {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}In your module's <module-name>.psm1 file, you can scan the module's functions & aliases and automatically export them when the module is imported. Paste this into a .psm1 module entrypoint:
## Set directory separator character, i.e. '\' on Windows
$DirectorySeparator = [System.IO.Path]::DirectorySeparatorChar
## Set name of module from $PSScriptRoot
$ModuleName = $PSScriptRoot.Split($DirectorySeparator)[-1]
## Define paths to public and private function directories
$PublicFunctionsPath = $PSScriptRoot + $DirectorySeparator + 'Functions' + $DirectorySeparator + 'Public' + $DirectorySeparator
$PrivateFunctionsPath = $PSScriptRoot + $DirectorySeparator + 'Functions' + $DirectorySeparator + 'Private' + $DirectorySeparator
## Path to Aliases.ps1
$AliasesFilePath = $PSScriptRoot + $DirectorySeparator + 'Aliases.ps1'
## Regular expression to match function definitions
$functionRegex = 'function\s+([^\s{]+)\s*\{'
## Get list of .ps1 files in Public/ recursively
$PublicFunctions = Get-ChildItem -Path $PublicFunctionsPath -Recurse -Filter *.ps1
## Get list of .ps1 files in Private/ recursively
$PrivateFunctions = Get-ChildItem -Path $PrivateFunctionsPath -Recurse -Filter *.ps1
## Load all private/internal PowerShell functions from script files
$PrivateFunctions | ForEach-Object {
. $_.FullName
}
$PublicFunctions | ForEach-Object {
. $_.FullName
}
## Gather function names from each script in the Public folder
$PublicFunctionNames = @()
foreach ($script in $PublicFunctions) {
$scriptContent = Get-Content -Path $script.FullName -Raw
$SearchMatches = [regex]::Matches($scriptContent, $functionRegex)
foreach ($match in $SearchMatches) {
$functionName = $match.Groups[1].Value
$PublicFunctionNames += $functionName
}
}
## Export each public function individually
$PublicFunctionNames | ForEach-Object {
Export-ModuleMember -Function $_
}
## Source the Aliases.ps1 file if it exists
if (Test-Path -Path $AliasesFilePath) {
. $AliasesFilePath
## Export aliases after sourcing the Aliases.ps1
$Aliases = Get-Command -CommandType Alias | Where-Object { $_.Source -eq $ModuleName }
$Aliases | ForEach-Object {
Export-ModuleMember -Alias $_.Name
}
}
Each Powershell module requires a unique GUID. You can generate a GUID with:
[guid]::NewGuid()If you have a script to update your module automatically, you can use this function to generate & save a GUID to a file in the module, or load from that file if it exists.
## Generate GUID if it doesn't exist
if ( -Not ( Test-Path -Path $GUIDFilePath ) ) {
Write-Debug "GUID file '$($GUIDFilePath)' does not exist. Generating GUID and saving to file."
$guid = [guid]::NewGuid().ToString()
Set-Content -Path $GUIDFilePath -Value $guid
} else {
Write-Debug "Loading GUID from file: $($GUIDFilePath)"
$guid = Get-Content -Path $GUIDFilePath
}When calling a script from within a script that receives -Debug and/or -Verbose switch params, you pass them a bit differently.
For example, script1.ps1 calls script2.ps1. Both scripts accept a [switch]$Debug and [switch]$Verbose parameter. To pass the values of $Debug and $Verbose to script2.ps1, use:
## script1.ps1
Param(
[switch]$Verbose,
[switch]$Debug
)
If ( $Debug ) {
$DebugPreference = "Continue"
}
If ( $Verbose ) {
$VerbosePreference = "Continue"
}
& .\script2.ps1 -Verbose:$Verbose -Debug:$DebugPassing switches as parameters uses this syntax: -SwitchParam:$SwitchParam.
When creating an alias, you can use an if ... else ... statement to detect the presence of a command or program, and only export the alias if it is present. For example, the code below creates an alias for lazygit called "lg":
## lg -> lazygit
Set-Alias -Name lg -Value lazygitYou can wrap this in an if statement that uses Get-Command to test if lazygit is installed, and will only export the alias if lazygit is present in the environment:
## lg -> lazygit
if ( Get-Command "lazygit" -ErrorAction SilentlyContinue ) {
## lazygit is installed, set the alias
Set-Alias -Name lg -Value lazygit
}
else {
## lazygit is not installed, unset the alias
Remove-Item -Path Alias:lg -ErrorAction SilentlyContinue
}