Skip to content

Instantly share code, notes, and snippets.

@chadbaldwin
Last active January 31, 2024 21:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadbaldwin/2ae7373ce6eb51c1b05b291c7dee40d8 to your computer and use it in GitHub Desktop.
Save chadbaldwin/2ae7373ce6eb51c1b05b291c7dee40d8 to your computer and use it in GitHub Desktop.
My personal PowerShell profile file
*
!Microsoft.*_profile.ps1
!.gitignore

PowerShell Profiles

# gist - https://gist.github.com/chadbaldwin/2ae7373ce6eb51c1b05b291c7dee40d8
######################################################
# Prompt customization
######################################################
oh-my-posh init pwsh | iex
# Commented out because it was causing errors in the terminal
# Set-PSReadLineOption -PredictionViewStyle ListView
######################################################
######################################################
# Script Imports
######################################################
######################################################
######################################################
# Add custom paths to PATH
######################################################
$env:Path = ((
@(
, 'C:\Program Files\Microsoft SQL Server\160\DAC\bin' # SqlPackage.exe
, 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\' # MSBuild + SSDT
, 'C:\Program Files\Rancher Desktop\resources\resources\win32\bin\' # Rancher - Docker CLI / nerdctl
, 'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\' # SQLCMD
, 'C:\Program Files (x86)\Red Gate\SQL Compare 15\' # sqlcompare
) +
$env:Path -split ';' |
gi -ErrorAction SilentlyContinue |
select @{N='Path'; E={[System.IO.Path]::TrimEndingDirectorySeparator($_)}} |
% Path
) | select -Unique) -join ';'
######################################################
######################################################
# Module Imports
######################################################
# Import-Module posh-git
Import-Module "${env:ChocolateyInstall}\helpers\chocolateyProfile.psm1" -ErrorAction SilentlyContinue
######################################################
######################################################
# Custom Git Functions
######################################################
# Run "git pull" on all Directories in the current directory
function Git-PullAll {
Get-ChildItem -Directory |
ForEach-Object {
$_.FullName
Start-Job -WorkingDirectory $_ -ScriptBlock { git pull } | Receive-Job -AutoRemoveJob -Wait
}
}
function Test-IsInsideGitRepo {
param (
[Parameter(Position=0)][string]$Path = '.\'
)
(git -C $Path rev-parse --is-inside-work-tree 2> $null) -EQ 'true' ? $true : $false
}
function Initialize-GitRepository {
git init -b 'main'
git add .
git commit -m 'init'
}
######################################################
######################################################
# Custom Docker Functions
######################################################
# gist - https://gist.github.com/chadbaldwin/1a486b4fbf48e9b0cd9551cb7e16382a
<# Docs:
https://hub.docker.com/_/microsoft-mssql-server
https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-configure-environment-variables
#>
# Start SQL Server docker instance
function Start-SQLServer {
param (
[Parameter()][string]$Tag = 'latest',
[Parameter()][string]$ContainerName = 'sqlserver',
[Parameter()][string]$Hostname,
[Parameter()][int]$Port = 1433,
[Parameter()][ValidateSet('Evaluation','Developer','Express','Web','Standard','Enterprise')]
[string]$Edition,
[Parameter()][switch]$EnableSQLServerAgent,
[Parameter()][string]$ContainerTimeZone
)
$sa_password = 'yourStrong(!)Password'
$container = docker inspect $ContainerName | ConvertFrom-Json
# create & start
if ($null -eq $container) {
# Build params to use for docker run call
$params = (
'--name', $ContainerName,
'-e', 'ACCEPT_EULA=Y',
'-e', "MSSQL_SA_PASSWORD=${sa_password}",
'-p', "${Port}:1433"
)
# optional params
if ($Hostname) { $params += '-h', $Hostname }
if ($Edition) { $params += '-e', "MSSQL_PID=$Edition" }
if ($EnableSQLServerAgent) { $params += '-e', "MSSQL_AGENT_ENABLED=${EnableSQLServerAgent}" }
if ($ContainerTimeZone) { $params += '-e', "TZ=${ContainerTimeZone}" }
docker run -d @params "mcr.microsoft.com/mssql/server:${Tag}"
if (!$LASTEXITCODE) { write "Container '${ContainerName}' container has been created and started" }
}
else {
$Tag = $container.Config.Image
# start
if ($container.State.Status -eq 'exited') {
docker start $containerName
write "Container '${ContainerName}' was already created and has been started"
}
# do nothing
elseif ($container.State.Status -eq 'running') {
write "Container '${ContainerName}' is already running"
}
$sa_password = (($container.Config.Env | ? { $_ -match 'MSSQL_SA_PASSWORD' }) -split '=')[1]
}
write '','Waiting until database is available...'
while ($true) {
docker exec $ContainerName /opt/mssql-tools/bin/sqlcmd -U sa -P $sa_password -l 1 -t 1 -Q 'SELECT 1' *>&1 | Out-Null
# Commenting out - using docker exec instead to remove dependency on sqlcmd being installed locally
# sqlcmd -U sa -P $sa_password -S localhost -l 1 -t 1 -Q 'SELECT 1' *>&1 | Out-Null
if ($LASTEXITCODE -eq 0) { break }
sleep 1
}
$container = docker inspect $ContainerName | ConvertFrom-Json
write '',"Image: ${Tag}",'',"SQL Server Version: $($container.Config.Labels.'com.microsoft.version')",''
scb $sa_password
write 'Password copied to clipboard:',' Username: sa'," Password: ${sa_password}"
}
Register-ArgumentCompleter -CommandName Start-SQLServer -ParameterName Tag -ScriptBlock {
(irm 'https://mcr.microsoft.com/v2/mssql/server/tags/list').tags | ? { $_ -match 'latest' }
}
######################################################
######################################################
# Other
######################################################
# pseudo sudo - Opens a new instance of the current shell under an admin account
function sudo {
$process = Get-Process -Id $PID
if ($process.Parent.Name -eq 'WindowsTerminal') {
<# Hard coded to use the PowerShell Core profile.
For some reason, Windows PowerShell doesn't seem to be aware
that it is running within Windows Terminal when pulling the
proess via Get-Process. #>
Start-Process 'wt' -ArgumentList "-p 'PowerShell' -d `"${PWD}`"" -Verb RunAs
} elseif ($process.Name -eq 'pwsh') {
Start-Process 'pwsh' -WorkingDirectory $PWD -Verb RunAs
} elseif ($process.Name -eq 'powershell') {
Start-Process 'powershell' -WorkingDirectory $PWD -Verb RunAs
}
}
# Sets the title of the current powershell window
function Set-Title($Title) {
$host.ui.RawUI.WindowTitle = $Title
}
# Removes all empty directories recursively in the current directory
function Remove-EmptyDirectories {
Get-ChildItem -Path $folder -Directory -Recurse | Where-Object { -Not $_.GetFiles('*',1) } | Remove-Item -Recurse
}
# Cleans build data and user settings from a visual studio solution folder
function Clean-SolutionFolder {
'bin','obj','.vs' | Get-ChildItem -Directory -Recurse -Force | Remove-Item -Force -Recurse
'*.user' | Get-ChildItem -File -Recurse | Remove-Item -Force -Recurse
}
# Shortcut for bypassing explorer...instead using Powershell and ZLocation.
function Invoke-QuickOpen {
param ([Parameter()][string]$Search)
z $Search
Invoke-Item .
exit
}
# Open the local .sln file. If there's multiple, bring them up in fzf
function Open-Solution {
$files = Get-ChildItem -Filter *.sln
if ($files.Count -eq 0) {
Write-Output 'No *.sln files found'
} elseif ($files.Count -eq 1) {
Invoke-Item "${files}"
} else {
$file = $files | Where-Object ToString | fzf
Invoke-Item "${file}"
}
}
# Bring window specified by process name into focus
function Set-ForegroundWindow {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)][string]$ProcessName,
[Parameter()][int]$ShowType
)
$type = '
using System;
using System.Runtime.InteropServices;
public class WinAp {
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}'
Add-Type $type
# Pick the first window that matches this process
$h = (Get-Process -Name $ProcessName | Where-Object MainWindowTitle | Select-Object -First 1).MainWindowHandle
# If you can't find the window, exit
if ($null -eq $h) { return }
# 2 = Minimize window
[WinAp]::ShowWindow($h, 2) # minimize it first...this is a hack so that we maintain snapped window positions
# 4 or 9 = Restore window, these are the only ones that seem to work predictably.
# They will restore a window to its original position if it is minimized, even if the window was snapped
# However, if the window is not minimized but is only out of focus, then it will unsnap the window.
# This is why we first minimize it, then restore it. It's hacky, but it works. It just looks a little funny.
[WinAp]::ShowWindow($h, 4)
# Bring window into focus
[WinAp]::SetForegroundWindow($h)
}
# Sort, dedup and overwrite clipboard contents
function Sort-Clipboard {
gcb | ? { $_.Trim() } | sort -Unique | scb
}
# Takes a pipeline input and groups it into batches of N size then returns the new arrays
function Get-Batches {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)][int]$BatchSize,
[Parameter(ValueFromPipeline = $true)][psobject]$InputObject
)
begin {
$currentBatch = @()
}
process {
$currentBatch += $_
if ($currentBatch.Count -eq $BatchSize) {
,$currentBatch
$currentBatch = @()
}
}
end {
if ($currentBatch.Count -gt 0) {
,$currentBatch
}
}
}
# Usage Examples:
# Write-Output (1..101) -NoEnumerate | Get-Batches -BatchSize 6 | % { $_ -join ',' }
# Get-Batches -BatchSize 6 -Array (1..101) | % { $_ -join ',' }
# Write-Output (1..101) -NoEnumerate | Get-Batches 6 | % { $_ -join ',' }
# Get-Batches 6 (1..101) | % { $_ -join ',' }
######################################################
######################################################
# Find system tools
######################################################
# Useful for finding things like msbuild.exe, vswhere.exe, sqlpackage.exe, etc
function Find-SystemTool {
param (
[Parameter(Mandatory,Position=0)][string]$Search
)
# Because we're searching a lot of system directories, we run into a lot of access denied errors. So for now
# it's easier to just ignore all and continue on. Maybe in the future I'll add better error handling.
$ErrorActionPreference = 'SilentlyContinue'
$list = @()
# Search PowerShell aware commands - this needs to be prioritized to indicate it can be called without including the full path
$list += Get-Command $Search -All |
Select-Object @{N='Path';E={$_.Source}},
Version,
@{N='Source';E={'Get-Command'}}, @{N='Rank';E={1}}
if (Get-Command es) {
# If system has `Everything` cli installed, use that instead since it is the fastest
$list += es !"${env:SystemRoot}" !"${env:USERPROFILE}\Appdata" "*${Search}" |
Get-Item |
Select-Object @{N='Path';E={$_.FullName}},
@{N='Version';E={$_.VersionInfo.ProductVersionRaw}},
@{N='Source';E={'Everything'}}, @{N='Rank';E={2}}
} else {
# Search all path directories - redundant with Get-Command, except for non-applications (modules, scripts, etc)
$list += $env:Path -split ';' |
Get-Item |
Get-ChildItem -File -Filter $Search |
Select-Object @{N='Path';E={$_.FullName}},
@{N='Version';E={$_.VersionInfo.ProductVersionRaw}},
@{N='Source';E={'Path'}}, @{N='Rank';E={2}}
# Search all common directories - this is by far the slowest because we are recursively searching many system folders
$list += $env:ProgramFiles, ${env:ProgramFiles(x86)}, $env:ProgramData,
$env:ChocolateyToolsLocation, $env:ChocolateyInstall |
Get-Item |
Get-ChildItem -File -Recurse -Filter $Search |
Select-Object @{N='Path';E={$_.FullName}},
@{N='Version';E={$_.VersionInfo.ProductVersionRaw}},
@{N='Source';E={'Manual'}}, @{N='Rank';E={3}}
}
# Dedup by path, select first by rank
$list |
Group-Object Path |
ForEach-Object { $_.Group | Sort-Object Rank | Select-Object -First 1 } |
Sort-Object Version -Descending |
Select-Object Path, Version, Source
}
######################################################
######################################################
# Aliases
######################################################
Set-Alias -Name zz -Value 'Invoke-QuickOpen'
Set-Alias -Name npp -Value "${Env:ProgramFiles(x86)}\Notepad++\notepad++.exe"
Set-Alias -Name w -Value 'Set-ForegroundWindow'
Set-Alias -Name query -Value 'Invoke-DbaQuery'
Set-Alias -Name sln -Value 'Open-Solution'
Set-Alias -Name omp -Value 'oh-my-posh.exe'
Set-Alias -Name igr -Value 'Initialize-GitRepository'
Set-Alias -Name ginit -Value 'Initialize-GitRepository'
######################################################
######################################################
######################################################
write "Profile: $($MyInvocation.MyCommand.Name)"
write "Loading: Microsoft.PowerShell_profile.ps1"
$userprofile = Join-Path ([Environment]::GetFolderPath("MyDocuments")) 'PowerShell\Microsoft.PowerShell_profile.ps1' | gi
. $userprofile
@chadbaldwin
Copy link
Author

[reserving first comment]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment