Skip to content

Instantly share code, notes, and snippets.

@david-jarman
Last active April 21, 2025 18:10
Show Gist options
  • Save david-jarman/bca0fe36ba699885c4156e8aeed8bbac to your computer and use it in GitHub Desktop.
Save david-jarman/bca0fe36ba699885c4156e8aeed8bbac to your computer and use it in GitHub Desktop.
Powershell Core custom profile
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
if ($host.Name -ne 'ConsoleHost')
{
# If not running in the console host, exit.
# This speeds up performance for non-interactive sessions.
return
}
Import-Module PSReadLine
Import-Module Terminal-Icons
Import-Module z
$localAppSettingsDir = Resolve-Path "~\AppData\Local"
$themeDir = "$localAppSettingsDir\Dev\themes"
$themeLocation = "$themeDir\stelbent.minimal.omp.json"
$themeGistId = "42a01c8ac0e44739065ce6af09987f01"
function Update-PoshTheme {
if (-not (Test-Path -Path $themeLocation)) {
gh gist clone $themeGistId $themeDir
}
else {
Push-Location $themeDir
git pull
Pop-Location
}
}
if (-not (Test-Path -Path $themeDir)) {
mkdir -Path $themeDir -ErrorAction Continue
Update-PoshTheme
}
oh-my-posh --init --shell pwsh --config $themeLocation | Invoke-Expression
$env:GH_EDITOR = 'code --wait'
$env:KUBE_EDITOR = 'code --wait'
$env:EDITOR = 'code --wait'
$env:PYTHONIOENCODING = 'utf8'
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -EditMode Windows
Set-PSReadLineOption -AddToHistoryHandler {
param($line)
# Don't add dotnet user-secrets commands to history
if ($line -match '^dotnet\s+user-secrets\s+set.*$') {
return $false
}
# if ($line -match 'llm\s+.*["'']') {
# return $false
# }
return $true
}
# Powershell Aliases
Set-Alias -Name k -Value kubectl.exe
Set-Alias -Name unzip -Value Expand-Archive
Set-Alias which Get-Command
Set-Alias touch New-Item
# Git aliases
git config --global alias.root 'rev-parse --show-toplevel' # git root now returns the root path of the current git repo
git config --global alias.undo 'reset HEAD^' # Undo the last commit
# Custom functions
function Test-Command {
param(
[string]$Command
)
try {
Get-Command $Command -ErrorAction Stop | Out-Null
}
catch {
return $false
}
return $true
}
function Encrypt-String {
$valueToEncrypt = Read-Host -AsSecureString -Prompt "Enter value to encrypt"
$key = Read-Host -AsSecureString -Prompt "Enter 16 character key"
return ConvertFrom-SecureString -SecureString $valueToEncrypt -SecureKey $key
}
function Decrypt-String {
param(
[string]$valueToDecrypt = ""
)
if ([string]::IsNullOrWhitespace($valueToDecrypt)) {
$valueToDecrypt = Read-Host -Prompt "Enter value to decrypt"
}
$key = Read-Host -AsSecureString -Prompt "Enter 16 character key"
$secureString = ConvertTo-SecureString -String $valueToDecrypt -SecureKey $key
return ConvertFrom-SecureString -SecureString $secureString -AsPlainText
}
function ConvertTo-Base64 {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$String
)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($String)
return [System.Convert]::ToBase64String($bytes)
}
function ConvertFrom-Base64 {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$Base64String
)
$bytes = [System.Convert]::FromBase64String($Base64String)
return [System.Text.Encoding]::UTF8.GetString($bytes)
}
function Generate-Password {
param(
[ValidateRange(16, 50)]
[int]$length=24
)
$symbols = '!@#$%^&*'.ToCharArray();
$characterList = 'a'..'z' + 'A'..'Z' + '0'..'9' + $symbols;
do {
$password = -join (0..($length-1) | % { $characterList | Get-Random });
[int]$hasLowerChar = $password -cmatch '[a-z]';
[int]$hasUpperChar = $password -cmatch '[A-Z]';
[int]$hasDigit = $password -match '[0-9]';
[int]$hasSymbol = $password.IndexOfAny($symbols) -ne -1;
} until (($hasLowerChar + $hasUpperChar + $hasDigit + $hasSymbol) -ge 3)
return $password | ConvertTo-SecureString -AsPlainText
}
function Show-NginxLogs {
$nginxControllerPod = kubectl get pods --selector 'app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller' --template '{{range .items}}{{.metadata.name}}{{end}}'
kubectl logs $nginxControllerPod --tail 100 -f
}
function Sanitize-Path {
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$Path
)
return $Path.Replace('\', '/')
}
function Update-Profile {
param(
[switch]$PushToRemote,
[string]$CommitMessage = "Updating profile from $($env:COMPUTERNAME)"
)
$profileGistId = "bca0fe36ba699885c4156e8aeed8bbac"
$profileFileName = "Microsoft.PowerShell_profile.ps1"
$localDir = "$PSScriptRoot\.temp-profile"
$remoteProfilePath = "$localDir\$profileFileName"
Write-Host "Cloning gist to temp folder"
gh gist clone $profileGistId $localDir
if ($PushToRemote) {
Write-Host "Pushing local profile to remote"
Get-Content $PROFILE | Out-File $remoteProfilePath
pushd $localDir
git add $profileFileName
git commit -m $CommitMessage
git push
popd
}
else {
Write-Host "Writing new profile to $PROFILE"
Get-Content $remoteProfilePath | Out-File $PROFILE
}
Remove-Item -Path $localDir -Force -Confirm:$false
}
$wingetStuff = @(
"JanDeDobbeleer.OhMyPosh",
"Git.Git",
"GitHub.cli",
"Microsoft.PowerToys",
"Microsoft.AzureCLI",
"Microsoft.DotNet.SDK",
"Helm.Helm",
"Microsoft.Azure.Kubelogin",
"Obsidian.Obsidian"
)
function Install-ProfileDependencies {
$wingetStuff | ForEach-Object {
Write-Host "Installing $_"
winget install $_
}
}
function Update-ProfileDependencies {
$wingetStuff | ForEach-Object {
Write-Host "Updating $_"
winget upgrade $_
}
}
# Git functions
function Get-DefaultBranch {
$defaultBranch = git remote show origin | Select-String -Pattern "HEAD branch" | ForEach-Object {
$_ -replace "HEAD branch: ", ""
}
return $defaultBranch.Trim()
}
function Rebase-LatestMaster {
$currentBranch = git branch --show-current
$defaultBranch = Get-DefaultBranch
git checkout $defaultBranch
git pull
git checkout $currentBranch
git rebase $defaultBranch
}
function Get-LatestDefaultBranch {
$defaultBranch = Get-DefaultBranch
Write-Host "Checking out $defaultBranch"
git checkout $defaultBranch;
Write-Host "Pulling latest"
git pull;
}
# Powershell functions
function Reload-Path {
# Get the system and user path
$systemPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine)
$userPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::User)
# Combine the system and user path
$combinedPath = $systemPath + ";" + $userPath
# Set the path in the shell
$env:Path = $combinedPath
}
function Refresh-TokenInDotEnvFile {
param(
[string]$Resource,
[string]$Key = 'JWT_TOKEN',
[string]$EnvFile = '.env'
)
$tokenJson = az account get-access-token --resource $Resource -o json | ConvertFrom-Json;
$token = $tokenJson.accessToken;
if (-not (Test-Path -Path $EnvFile)) {
New-Item -Path $EnvFile -ItemType File
"$Key=$token" | Out-File $EnvFile -Encoding "UTF8"
}
else {
$envFileContent = Get-Content $EnvFile
$envFileContent | ForEach-Object {
if ($_ -match "^$Key=") {
"$Key=$token"
}
else {
$_
}
} | Set-Content $EnvFile
}
}
# Other functions
# Run a powershell expression as root
# Usage: Sudo-Script { <expression> }
function Sudo-Script {
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = $true)]
[scriptblock]$ScriptBlock
)
sudo pwsh -c "$($ScriptBlock.ToString()) && Read-Host 'Press enter to continue...'"
}
# HTML helpers
function Get-WebsiteTitle {
param(
[string]$url
)
shot-scraper javascript $url "document.title"
}
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
# Performance profiling configuration
$script:ProfilePerformanceEnabled = $true
$script:ProfilePerformanceTimings = [System.Collections.Generic.List[PSCustomObject]]::new()
function Measure-ProfileSection {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$SectionName,
[Parameter(Mandatory = $true)]
[scriptblock]$ScriptBlock
)
if ($script:ProfilePerformanceEnabled) {
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
& $ScriptBlock
$stopwatch.Stop()
$script:ProfilePerformanceTimings.Add([PSCustomObject]@{
Section = $SectionName
Time = $stopwatch.ElapsedMilliseconds
})
} else {
& $ScriptBlock
}
}
function Enable-ProfilePerformance {
$script:ProfilePerformanceEnabled = $true
$script:ProfilePerformanceTimings.Clear()
Write-Host "Profile performance measurement enabled."
}
function Disable-ProfilePerformance {
$script:ProfilePerformanceEnabled = $false
Write-Host "Profile performance measurement disabled."
}
function Show-ProfilePerformance {
if ($script:ProfilePerformanceTimings.Count -eq 0) {
Write-Host "No performance data available. Run Enable-ProfilePerformance and reload your profile."
return
}
$totalTime = ($script:ProfilePerformanceTimings | Measure-Object -Property Time -Sum).Sum
Write-Host "Profile Performance Report" -ForegroundColor Cyan
Write-Host "------------------------" -ForegroundColor Cyan
$script:ProfilePerformanceTimings |
Sort-Object -Property Time -Descending |
ForEach-Object {
$percentage = [math]::Round(($_.Time / $totalTime) * 100, 1)
Write-Host ("{0,-30} {1,6} ms ({2,5}%)" -f $_.Section, $_.Time, $percentage)
}
Write-Host "------------------------" -ForegroundColor Cyan
Write-Host "Total time: $totalTime ms" -ForegroundColor Cyan
}
# Begin profile execution with performance measurement
Measure-ProfileSection "PSReadLine Import" {
if ($host.Name -eq 'ConsoleHost')
{
Import-Module PSReadLine
}
}
Measure-ProfileSection "Theme Setup" {
$localAppSettingsDir = Resolve-Path "~\AppData\Local"
$themeDir = "$localAppSettingsDir\Dev\themes"
$themeLocation = "$themeDir\stelbent.minimal.omp.json"
function Update-PoshTheme {
$themeGistId = "42a01c8ac0e44739065ce6af09987f01"
if (-not (Test-Path -Path $themeLocation)) {
gh gist clone $themeGistId $themeDir
}
else {
Push-Location $themeDir
git pull
Pop-Location
}
}
if (-not (Test-Path -Path $themeDir)) {
mkdir -Path $themeDir -ErrorAction Continue
Update-PoshTheme
}
oh-my-posh --init --shell pwsh --config $themeLocation | Invoke-Expression
}
Measure-ProfileSection "Import Terminal-Icons module" {
Import-Module Terminal-Icons
}
Measure-ProfileSection "Import z module" {
Import-Module z
}
Measure-ProfileSection "Environment Setup" {
$env:GH_EDITOR = "code --wait"
$env:KUBE_EDITOR = "code --wait"
}
Measure-ProfileSection "Register autocompleter for winget" {
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
}
Measure-ProfileSection "PSReadLine Configuration" {
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -EditMode Windows
Set-PSReadLineOption -AddToHistoryHandler {
param($line)
# Don't add dotnet user-secrets commands to history
if ($line -match '^dotnet\s+user-secrets\s+set.*$') {
return $false
}
# Don't add llm commands to history (turning off for now)
# if ($line -match 'llm\s+.*["'']') {
# return $false
# }
return $true
}
}
# Powershell Aliases
Measure-ProfileSection "Create Powershell aliases" {
Set-Alias -Name k -Value kubectl.exe
Set-Alias -Name unzip -Value Expand-Archive
Set-Alias which Get-Command
Set-Alias touch New-Item
}
# Git aliases
Measure-ProfileSection "Create Git aliases" {
git config --global alias.root 'rev-parse --show-toplevel' # git root now returns the root path of the current git repo
git config --global alias.undo 'reset HEAD^' # Undo the last commit
}
# Custom functions
function Test-Command {
param(
[string]$Command
)
try {
Get-Command $Command -ErrorAction Stop | Out-Null
}
catch {
return $false
}
return $true
}
function Encrypt-String {
$valueToEncrypt = Read-Host -AsSecureString -Prompt "Enter value to encrypt"
$key = Read-Host -AsSecureString -Prompt "Enter 16 character key"
return ConvertFrom-SecureString -SecureString $valueToEncrypt -SecureKey $key
}
function Decrypt-String {
param(
[string]$valueToDecrypt = ""
)
if ([string]::IsNullOrWhitespace($valueToDecrypt)) {
$valueToDecrypt = Read-Host -Prompt "Enter value to decrypt"
}
$key = Read-Host -AsSecureString -Prompt "Enter 16 character key"
$secureString = ConvertTo-SecureString -String $valueToDecrypt -SecureKey $key
return ConvertFrom-SecureString -SecureString $secureString -AsPlainText
}
function ConvertTo-Base64 {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$String
)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($String)
return [System.Convert]::ToBase64String($bytes)
}
function ConvertFrom-Base64 {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$Base64String
)
$bytes = [System.Convert]::FromBase64String($Base64String)
return [System.Text.Encoding]::UTF8.GetString($bytes)
}
function Generate-Password {
param(
[ValidateRange(16, 50)]
[int]$length=24
)
$symbols = '!@#$%^&*'.ToCharArray();
$characterList = 'a'..'z' + 'A'..'Z' + '0'..'9' + $symbols;
do {
$password = -join (0..($length-1) | % { $characterList | Get-Random });
[int]$hasLowerChar = $password -cmatch '[a-z]';
[int]$hasUpperChar = $password -cmatch '[A-Z]';
[int]$hasDigit = $password -match '[0-9]';
[int]$hasSymbol = $password.IndexOfAny($symbols) -ne -1;
} until (($hasLowerChar + $hasUpperChar + $hasDigit + $hasSymbol) -ge 3)
return $password | ConvertTo-SecureString -AsPlainText
}
function Show-NginxLogs {
$nginxControllerPod = kubectl get pods --selector 'app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller' --template '{{range .items}}{{.metadata.name}}{{end}}'
kubectl logs $nginxControllerPod --tail 100 -f
}
function Sanitize-Path {
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]$Path
)
return $Path.Replace('\', '/')
}
function Update-Profile {
param(
[switch]$PushToRemote,
[string]$CommitMessage = "Updating profile from $($env:COMPUTERNAME)"
)
$profileGistId = "bca0fe36ba699885c4156e8aeed8bbac"
$profileFileName = "Microsoft.PowerShell_profile.ps1"
$localDir = "$PSScriptRoot\.temp-profile"
$remoteProfilePath = "$localDir\$profileFileName"
Write-Host "Cloning gist to temp folder"
gh gist clone $profileGistId $localDir
if ($PushToRemote) {
Write-Host "Pushing local profile to remote"
Get-Content $PROFILE | Out-File $remoteProfilePath
pushd $localDir
git add $profileFileName
git commit -m $CommitMessage
git push
popd
}
else {
Write-Host "Writing new profile to $PROFILE"
Get-Content $remoteProfilePath | Out-File $PROFILE
}
Remove-Item -Path $localDir -Force -Confirm:$false
}
$wingetStuff = @(
"JanDeDobbeleer.OhMyPosh",
"Git.Git",
"GitHub.cli",
"Microsoft.PowerToys",
"Microsoft.AzureCLI",
"Microsoft.DotNet.SDK",
"Helm.Helm",
"Microsoft.Azure.Kubelogin",
"Obsidian.Obsidian"
)
function Install-ProfileDependencies {
$wingetStuff | ForEach-Object {
Write-Host "Installing $_"
winget install $_
}
}
function Update-ProfileDependencies {
$wingetStuff | ForEach-Object {
Write-Host "Updating $_"
winget upgrade $_
}
}
# Git functions
function Get-DefaultBranch {
$defaultBranch = git remote show origin | Select-String -Pattern "HEAD branch" | ForEach-Object {
$_ -replace "HEAD branch: ", ""
}
return $defaultBranch.Trim()
}
function Rebase-LatestMaster {
$currentBranch = git branch --show-current
$defaultBranch = Get-DefaultBranch
git checkout $defaultBranch
git pull
git checkout $currentBranch
git rebase $defaultBranch
}
function Get-LatestDefaultBranch {
$defaultBranch = Get-DefaultBranch
Write-Host "Checking out $defaultBranch"
git checkout $defaultBranch;
Write-Host "Pulling latest"
git pull;
}
# Powershell functions
function Reload-Path {
# Get the system and user path
$systemPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine)
$userPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::User)
# Combine the system and user path
$combinedPath = $systemPath + ";" + $userPath
# Set the path in the shell
$env:Path = $combinedPath
}
function Refresh-TokenInDotEnvFile {
param(
[string]$Resource,
[string]$Key = 'JWT_TOKEN',
[string]$EnvFile = '.env'
)
$tokenJson = az account get-access-token --resource $Resource -o json | ConvertFrom-Json;
$token = $tokenJson.accessToken;
if (-not (Test-Path -Path $EnvFile)) {
New-Item -Path $EnvFile -ItemType File
"$Key=$token" | Out-File $EnvFile -Encoding "UTF8"
}
else {
$envFileContent = Get-Content $EnvFile
$envFileContent | ForEach-Object {
if ($_ -match "^$Key=") {
"$Key=$token"
}
else {
$_
}
} | Set-Content $EnvFile
}
}
# Other functions
# Run a powershell expression as root
# Usage: Sudo-Script { <expression> }
function Sudo-Script {
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = $true)]
[scriptblock]$ScriptBlock
)
sudo pwsh -c "$($ScriptBlock.ToString()) && Read-Host 'Press enter to continue...'"
}
# Function to generate a regex for an acronym
function Get-AcronymRegex {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Enter the acronym to generate the regex for")]
[string]$Acronym
)
# Escape each letter of the acronym and create a regex pattern
$pattern = ($Acronym.ToCharArray() | ForEach-Object { "(?i:\b$_\w*\b)" }) -join '\s+'
return $pattern
}
if ($script:ProfilePerformanceEnabled) {
Show-ProfilePerformance
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment