PSProfile.psm1
enum PSProfileLogLevel { Information Warning Error Debug Verbose Quiet } enum PSProfileSecretType { PSCredential SecureString } class PSProfileEvent { hidden [datetime] $Time [timespan] $Total [timespan] $Last [PSProfileLogLevel] $LogLevel [string] $Section [string] $Message PSProfileEvent( [datetime] $time, [timespan] $last, [timespan] $total, [PSProfileLogLevel] $logLevel, [string] $section, [string] $message ) { $this.Time = $time $this.Last = $last $this.Total = $total $this.Section = $section $this.Message = $message $this.LogLevel = $logLevel } } class PSProfileSecret { [PSProfileSecretType] $Type hidden [pscredential] $PSCredential hidden [securestring] $SecureString PSProfileSecret([string]$userName, [securestring]$password) { $this.Type = [PSProfileSecretType]::PSCredential $this.PSCredential = [PSCredential]::new($userName,$password) } PSProfileSecret([pscredential]$psCredential) { $this.Type = [PSProfileSecretType]::PSCredential $this.PSCredential = $psCredential } PSProfileSecret([SecureString]$secureString) { $this.Type = [PSProfileSecretType]::SecureString $this.SecureString = $secureString } } class PSProfileVault : Hashtable { [hashtable] $_secrets PSProfileVault() { $this._secrets = @{ } } [void] SetSecret([string]$name, [string]$userName, [securestring]$password) { $this._secrets[$name] = [PSCredential]::new( $userName, $password ) } [void] SetSecret([pscredential]$psCredential) { $this._secrets[$psCredential.UserName] = $psCredential } [void] SetSecret([string]$name, [pscredential]$psCredential) { $this._secrets[$name] = $psCredential } [void] SetSecret([string]$name, [securestring]$secureString) { $this._secrets[$name] = $secureString } [pscredential] GetSecret() { if ($env:USERNAME) { return $this._secrets[$env:USERNAME] } elseif ($env:USER) { return $this._secrets[$env:USER] } else { return $null } } [object] GetSecret([string]$name) { return $this._secrets[$name] } [void] RemoveSecret([string]$name) { $this._secrets.Remove($name) } } class PSProfile { hidden [System.Collections.Generic.List[PSProfileEvent]] $Log [hashtable] $_internal [hashtable] $Settings [datetime] $LastRefresh [string] $RefreshFrequency [hashtable] $GitPathMap [hashtable] $PSBuildPathMap [object[]] $ModulesToImport [object[]] $ModulesToInstall [hashtable] $PathAliases [hashtable] $CommandAliases [hashtable[]] $Plugins [string[]] $PluginPaths [string[]] $ProjectPaths [hashtable] $Prompts [string[]] $ScriptPaths [hashtable] $SymbolicLinks [hashtable] $Variables [PSProfileVault] $Vault PSProfile() { $this.Log = [System.Collections.Generic.List[PSProfileEvent]]::new() $this.Vault = [PSProfileVault]::new() $this._internal = @{ } $this.GitPathMap = @{ PSProfileConfiguration = (Join-Path (Get-ConfigurationPath -CompanyName 'SCRT HQ' -Name PSProfile) 'Configuration.psd1') } $this.PSBuildPathMap = @{ } $this.SymbolicLinks = @{ } $this.Prompts = @{ Default = '"PS $($executionContext.SessionState.Path.CurrentLocation)$(">" * ($nestedPromptLevel + 1)) "; # .Link # https://go.microsoft.com/fwlink/?LinkID=225750 # .ExternalHelp System.Management.Automation.dll-help.xml' SCRTHQ = '$lastStatus = $? $lastColor = if ($lastStatus -eq $true) { "Green" } else { "Red" } Write-Host "[" -NoNewline Write-Host -ForegroundColor Cyan "#$($MyInvocation.HistoryId)" -NoNewline Write-Host "] " -NoNewline Write-Host "[" -NoNewLine $verColor = @{ ForegroundColor = if ($PSVersionTable.PSVersion.Major -eq 7) { "Yellow" } elseif ($PSVersionTable.PSVersion.Major -eq 6) { "Magenta" } else { "Cyan" } } Write-Host @verColor ("PS {0}" -f (Get-PSVersion)) -NoNewline Write-Host "] " -NoNewline Write-Host "[" -NoNewline Write-Host -ForegroundColor $lastColor ("{0}" -f (Get-LastCommandDuration)) -NoNewline Write-Host "] [" -NoNewline Write-Host ("{0}" -f $(Get-PathAlias)) -NoNewline -ForegroundColor DarkYellow Write-Host "]" -NoNewline if ($PWD.Path -notlike "\\*" -and $env:DisablePoshGit -ne $true -and (Test-IfGit)) { Write-VcsStatus $GitPromptSettings.EnableWindowTitle = "PS {0} @" -f (Get-PSVersion) } else { $Host.UI.RawUI.WindowTitle = "PS {0}" -f (Get-PSVersion) } "`n>> "' } $this.Variables = @{ Environment = @{ Home = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) UserName = [System.Environment]::UserName ComputerName = [System.Environment]::MachineName } Global = @{ PathAliasDirectorySeparator = "$([System.IO.Path]::DirectorySeparatorChar)" AltPathAliasDirectorySeparator = "$([char]0xe0b1)" } } $this.Settings = @{ DefaultPrompt = 'Default' PSVersionStringLength = 3 } $this.RefreshFrequency = (New-Timespan -Hours 1).ToString() $this.LastRefresh = [datetime]::Now.AddHours(-2) $this.ProjectPaths = @() $this.PluginPaths = @( (Join-Path $PSScriptRoot "Plugins") ) $this.ScriptPaths = @() $this.PathAliases = @{ '~' = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) } $this.CommandAliases = @{} } [void] Load() { $this._internal['ProfileLoadStart'] = [datetime]::Now $this._log( "SECTION START", "MAIN", "Debug" ) $this._loadConfiguration() if (([datetime]::Now - $this.LastRefresh) -gt [timespan]$this.RefreshFrequency) { $withRefresh = ' with refresh.' $this.Refresh() } else { $withRefresh = '.' $this._log( "Skipped full refresh! Frequency set to '$($this.RefreshFrequency)', but last refresh was: $($this.LastRefresh.ToString())", "MAIN", "Verbose" ) } if ($Global:PSDefaultParameterValues.Keys.Count) { $Global:PSDefaultParameterValues['Import-Module:Global'] = $true $Global:PSDefaultParameterValues['Import-Module:Force'] = $true $Global:PSDefaultParameterValues['Import-Module:Verbose'] = $false } else { $Global:PSDefaultParameterValues = @{ 'Import-Module:Global' = $true 'Import-Module:Force' = $true 'Import-Module:Verbose' = $false } } $this._importModules() $this._loadPlugins() $this._invokeScripts() $this._setVariables() $this._setCommandAliases() $this._loadPrompt() $this._internal['ProfileLoadEnd'] = [datetime]::Now $this._internal['ProfileLoadDuration'] = $this._internal.ProfileLoadEnd - $this._internal.ProfileLoadStart $this._log( "SECTION END", "MAIN", "Debug" ) Write-Host "Loading PSProfile alone took $([Math]::Round($this._internal.ProfileLoadDuration.TotalMilliseconds))ms$withRefresh" } [void] Refresh() { $this._log( "Refreshing project map, checking for modules to install and creating symbolic links", "MAIN", "Verbose" ) $this._findProjects() $this._installModules() $this._createSymbolicLinks() $this._formatPrompts() $this.LastRefresh = [datetime]::Now $this.Save() } [void] Save() { $out = @{ } $this.PSObject.Properties.Name | Where-Object { $_ -ne '_internal' } | ForEach-Object { $out[$_] = $this.$_ } $out | Export-Configuration -Name PSProfile -CompanyName 'SCRT HQ' } hidden [string] _globalize([string]$content) { $noScopePattern = 'function\s+(?<Name>[\w+_-]{1,})\s+\{' $globalScopePattern = 'function\s+global\:' $noScope = [RegEx]::Matches($content, $noScopePattern, "Multiline, IgnoreCase") $globalScope = [RegEx]::Matches($content,$globalScopePattern,"Multiline, IgnoreCase") if ($noScope.Count -ge $globalScope.Count) { foreach ($match in $noScope) { $fullValue = ($match.Groups | Where-Object { $_.Name -eq 0 }).Value $funcName = ($match.Groups | Where-Object { $_.Name -eq 'Name' }).Value $content = $content.Replace($fullValue, "function global:$funcName {") } } return $content } hidden [void] _loadPrompt() { $this._log( "SECTION START", "LoadPrompt", "Debug" ) if ($null -ne $global:PSProfile.Settings.DefaultPrompt) { Switch-PSProfilePrompt -Name $global:PSProfile.Settings.DefaultPrompt } $this._log( "SECTION END", "LoadPrompt", "Debug" ) } hidden [void] _formatPrompts() { $this._log( "SECTION START", "FormatPrompts", "Debug" ) $pssa = if ($null -eq (Get-Module PSScriptAnalyzer* -ListAvailable)) { $false } else { $true Import-Module PSScriptAnalyzer } $final = @{} $Global:PSProfile.Prompts.GetEnumerator() | ForEach-Object { $updated = if ($pssa) { $this._log( "Formatting prompt '$($_.Key)' via Invoke-Formatter", "FormatPrompts", "Verbose" ) (Invoke-Formatter $_.Value -Verbose:$false) -join "`n" } else { $this._log( "Formatting prompt '$($_.Key)' via Trim() (PSScriptAnalyzer not found)", "FormatPrompts", "Verbose" ) ($_.Value -split "[\r\n]" | Where-Object {$_}).Trim() -join "`n" } $final[$_.Key] = $updated } $Global:PSProfile.Prompts = $final $this._log( "SECTION END", "FormatPrompts", "Debug" ) } hidden [void] _loadAdditionalConfiguration([string]$configurationPath) { $this._log( "SECTION START", "AddlConfiguration", "Debug" ) $this._log( "Importing additional file: $configurationPath", "AddlConfiguration", "Verbose" ) $additional = Import-Metadata -Path $configurationPath $this._log( "Adding additional configuration to PSProfile object", "AddlConfiguration", "Verbose" ) $this | Update-Object $additional $this._log( "SECTION END", "AddlConfiguration", "Debug" ) } hidden [void] _loadConfiguration() { $this._log( "SECTION START", "Configuration", "Debug" ) $this._log( "Importing layered Configuration", "Configuration", "Verbose" ) $conf = Import-Configuration -Name PSProfile -CompanyName 'SCRT HQ' -DefaultPath (Join-Path $PSScriptRoot "Configuration.psd1") $this._log( "Adding layered configuration to PSProfile object", "Configuration", "Verbose" ) $this | Update-Object $conf $this._log( "SECTION END", "Configuration", "Debug" ) } hidden [void] _setCommandAliases() { $this._log( "SECTION START", 'SetCommandAliases', 'Debug' ) $this.CommandAliases.GetEnumerator() | ForEach-Object { try { $Name = $_.Key $Value = $_.Value if ($null -eq (Get-Alias "$Name*")) { New-Alias -Name $Name -Value $Value -Scope Global -Option AllScope -ErrorAction Stop $this._log( "Set command alias: $Name > $Value", 'SetCommandAliases', 'Verbose' ) } else { $this._log( "Alias already in use, skipping: $Name", 'SetCommandAliases', 'Verbose' ) } } catch { $this._log( "Failed to set command alias: $Name > $Value :: $($_)", 'SetCommandAliases', 'Warning' ) } } $this._log( "SECTION END", 'SetCommandAliases', 'Debug' ) } hidden [void] _createSymbolicLinks() { $this._log( "SECTION START", 'CreateSymbolicLinks', 'Debug' ) if ($null -ne $this.SymbolicLinks.Keys) { $null = $this.SymbolicLinks.GetEnumerator() | Start-RSJob -Name { "_PSProfile_SymbolicLinks_" + $_.Key } -ScriptBlock { if (-not (Test-Path $_.Key) -or ((Get-Item $_.Key).LinkType -eq 'SymbolicLink' -and (Get-Item $_.Key).Target -ne $_.Value)) { New-Item -ItemType SymbolicLink -Path $_.Key -Value $_.Value -Force } } } else { $this._log( "No symbolic links specified!", 'CreateSymbolicLinks', 'Verbose' ) } $this._log( "SECTION END", 'CreateSymbolicLinks', 'Debug' ) } hidden [void] _setVariables() { $this._log( "SECTION START", 'SetVariables', 'Debug' ) if ($null -ne $this.Variables.Keys) { foreach ($varType in $this.Variables.Keys) { switch ($varType) { Environment { $this.Variables.Environment.GetEnumerator() | ForEach-Object { $this._log( "`$env:$($_.Key) = '$($_.Value)'", 'SetVariables', 'Verbose' ) Set-Item "Env:\$($_.Key)" -Value $_.Value -Force } } default { $this.Variables.Global.GetEnumerator() | ForEach-Object { $this._log( "`$global:$($_.Key) = '$($_.Value)'", 'SetVariables', 'Verbose' ) Set-Variable -Name $_.Key -Value $_.Value -Scope Global -Force } } } } } else { $this._log( "No variables key/value pairs provided!", 'SetVariables', 'Verbose' ) } $this._log( "SECTION END", 'SetVariables', 'Debug' ) } hidden [void] _findProjects() { $this._log( "SECTION START", 'FindProjects', 'Debug' ) if (-not [string]::IsNullOrEmpty((-join $this.ProjectPaths))) { $this.GitPathMap = @{ } $this.ProjectPaths | ForEach-Object { $p = $_ $cnt = 0 if (Test-Path $p) { $p = (Resolve-Path $p).Path $cnt++ $pInfo = [System.IO.DirectoryInfo]::new($p) $this.PathAliases["@$($pInfo.Name)"] = $pInfo.FullName $this._log( "Added path alias: @$($pInfo.Name) >> $($pInfo.FullName)", 'FindProjects', 'Verbose' ) $g = 0 $b = 0 $pInfo.EnumerateDirectories('.git',[System.IO.SearchOption]::AllDirectories) | ForEach-Object { $g++ $this._log( "Found git project @ $($_.Parent.FullName)", 'FindProjects', 'Verbose' ) $this.GitPathMap[$_.Parent.BaseName] = $_.Parent.FullName $bldPath = [System.IO.Path]::Combine($_.Parent.FullName,'build.ps1') if ([System.IO.File]::Exists($bldPath)) { $b++ $this._log( "Found build script @ $($_.FullName)", 'FindProjects', 'Verbose' ) $this.PSBuildPathMap[$_.Parent.BaseName] = $_.Parent.FullName } } $this._log( "$p :: Found: $g git | $b build", 'FindProjects', 'Verbose' ) } else { $this._log( "'$p' Unable to resolve path!", 'FindProjects', 'Verbose' ) } } } else { $this._log( "No project paths specified to search in!", 'FindProjects', 'Verbose' ) } $this._log( "SECTION END", 'FindProjects', 'Debug' ) } hidden [void] _invokeScripts() { $this._log( "SECTION START", 'InvokeScripts', 'Debug' ) if (-not [string]::IsNullOrEmpty((-join $this.ScriptPaths))) { $this.ScriptPaths | ForEach-Object { $p = $_ if (Test-Path $p) { $i = Get-Item $p $p = $i.FullName if ($p -match '\.ps1$') { try { $this._log( "'$($i.Name)' Invoking script", 'InvokeScripts', 'Verbose' ) $sb = [scriptblock]::Create($this._globalize(([System.IO.File]::ReadAllText($i.FullName)))) .$sb } catch { $e = $_ $this._log( "'$($i.Name)' Failed to invoke script! Error: $e", 'InvokeScripts', 'Warning' ) } } else { [System.IO.DirectoryInfo]::new($p).EnumerateFiles('*.ps1',[System.IO.SearchOption]::AllDirectories) | Where-Object { $_.BaseName -notmatch '^(profile|CONFIG|WIP)' } | ForEach-Object { $s = $_ try { $this._log( "'$($s.Name)' Invoking script", 'InvokeScripts', 'Verbose' ) $sb = [scriptblock]::Create($this._globalize(([System.IO.File]::ReadAllText($s.FullName)))) .$sb } catch { $e = $_ $this._log( "'$($s.Name)' Failed to invoke script! Error: $e", 'InvokeScripts', 'Warning' ) } } } } else { $this._log( "'$p' Unable to resolve path!", 'FindProjects', 'Verbose' ) } } } else { $this._log( "No script paths specified to invoke!", 'InvokeScripts', 'Verbose' ) } $this._log( "SECTION END", 'InvokeScripts', 'Debug' ) } hidden [void] _installModules() { $this._log( "SECTION START", 'InstallModules', 'Debug' ) if (-not [string]::IsNullOrEmpty((-join $this.ModulesToInstall))) { $null = $this.ModulesToInstall | Start-RSJob -Name { "_PSProfile_InstallModule_$($_)" } -VariablesToImport this -ScriptBlock { Param ( [parameter()] [object] $Module ) $params = if ($Module -is [string]) { @{Name = $Module } } elseif ($Module -is [hashtable]) { $Module } else { $null } $this._log( "Checking if module is installed already: $($params | ConvertTo-Json -Compress)", 'InstallModules', 'Verbose' ) if ($null -eq (Get-Module $params['Name'] -ListAvailable)) { $this._log( "Installing missing module to CurrentUser scope: $($params | ConvertTo-Json -Compress)", 'InstallModules', 'Verbose' ) Install-Module -Name @params -Scope CurrentUser -AllowClobber -SkipPublisherCheck } else { $this._log( "Module already installed, skipping: $($params | ConvertTo-Json -Compress)", 'InstallModules', 'Verbose' ) } } } else { $this._log( "No modules specified to install!", 'InstallModules', 'Verbose' ) } $this._log( "SECTION END", 'InstallModules', 'Debug' ) } hidden [void] _importModules() { $this._log( "SECTION START", 'ImportModules', 'Debug' ) if (-not [string]::IsNullOrEmpty((-join $this.ModulesToImport))) { $this.ModulesToImport | ForEach-Object { try { $params = if ($_ -is [string]) { @{Name = $_ } } elseif ($_ -is [hashtable]) { $_ } else { $null } if ($null -ne $params) { @('ErrorAction','Verbose') | ForEach-Object { if ($params.ContainsKey($_)) { $params.Remove($_) } } Import-Module @params -Global -ErrorAction SilentlyContinue -Verbose:$false $this._log( "Module imported: $($params | ConvertTo-Json -Compress)", 'ImportModules', 'Verbose' ) } else { $this._log( "Module must be either a string or a hashtable!", 'ImportModules', 'Verbose' ) } } catch { $this._log( "'$($params['Name'])' Error importing module: $($Error[0].Exception.Message)", "ImportModules", "Warning" ) } } } else { $this._log( "No modules specified to import!", 'ImportModules', 'Verbose' ) } $this._log( "SECTION END", 'ImportModules', 'Debug' ) } hidden [void] _loadPlugins() { $this._log( "SECTION START", 'LoadPlugins', 'Debug' ) if ($this.Plugins.Count) { $this.Plugins.ForEach( { $plugin = $_ $this._log( "'$($plugin.Name)' Searching for plugin", 'LoadPlugins', 'Verbose' ) try { $found = $null $importParams = @{ ErrorAction = 'Stop' } if ($plugin.ArgumentList) { $importParams['ArgumentList'] = $plugin.ArgumentList } foreach ($plugPath in $this.PluginPaths) { $fullPath = [System.IO.Path]::Combine($plugPath,"$($plugin.Name).ps1") $this._log( "'$($plugin.Name)' Checking path: $fullPath", 'LoadPlugins', 'Verbose' ) if (Test-Path $fullPath) { $sb = [scriptblock]::Create($this._globalize(([System.IO.File]::ReadAllText($fullPath)))) if ($plugin.ArgumentList) { .$sb($plugin.ArgumentList) } else { .$sb } $found = $fullPath break } } if ($null -ne $found) { $this._log( "'$($plugin.Name)' plugin loaded from path: $found", 'LoadPlugins' ) } else { if ($null -ne (Get-Module $plugin.Name -ListAvailable -ErrorAction SilentlyContinue)) { Import-Module $plugin.Name @importParams $this._log( "'$($plugin.Name)' plugin loaded from PSModulePath!", 'LoadPlugins' ) } else { $this._log( "'$($plugin.Name)' plugin not found!", 'LoadPlugins', 'Warning' ) } } } catch { throw } }) } else { $this._log( "No plugins specified to load!", 'LoadPlugins', 'Verbose' ) } $this._log( "SECTION END", 'LoadPlugins', 'Debug' ) } hidden [void] _log([string]$message,[string]$section,[PSProfileLogLevel]$logLevel) { $dt = Get-Date $shortMessage = "[$($dt.ToString('HH:mm:ss'))] $message" $lastCommand = if ($this.Log.Count) { $dt - $this.Log[-1].Time } else { New-TimeSpan } $this.Log.Add( [PSProfileEvent]::new( $dt, $lastCommand, ($dt - $this._internal.ProfileLoadStart), $logLevel, $section, $message ) ) switch ($logLevel) { Information { Write-Host $shortMessage } Verbose { Write-Verbose $shortMessage } Warning { Write-Warning $shortMessage } Error { Write-Error $shortMessage } Debug { Write-Debug $shortMessage } } } hidden [void] _log([string]$message,[string]$section) { $this._log($message,$section,'Quiet') } } function Decrypt { param($Item) if ($Item -is [System.Security.SecureString]) { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $Item ) ) } else { $Item } } function Encrypt { param($Item) if ($Item -is [System.Security.SecureString]) { $Item } elseif ($Item -is [System.String] -and -not [System.String]::IsNullOrWhiteSpace($Item)) { ConvertTo-SecureString -String $Item -AsPlainText -Force } } function Add-PSProfileCommandAlias { <# .SYNOPSIS Adds a command alias to your PSProfile configuration to set during PSProfile import. .DESCRIPTION Adds a command alias to your PSProfile configuration to set during PSProfile import. .PARAMETER Alias The alias to set for the command. .PARAMETER Command The name of the command to set the alias for. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileCommandAlias -Alias code -Command Open-Code -Save Adds the command alias 'code' targeting the command 'Open-Code' and saves your PSProfile configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [String] $Alias, [Parameter(Mandatory,Position = 1)] [String] $Command, [Parameter()] [Switch] $Save ) Process { Write-Verbose "Adding alias '$Alias' for command '$Command' to PSProfile" New-Alias -Name $Alias -Value $Command -Option AllScope -Scope Global $Global:PSProfile.CommandAliases[$Alias] = $Command if ($Save) { Save-PSProfile } } } Register-ArgumentCompleter -CommandName Get-PSProfileCommandAlias -ParameterName Command -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) (Get-Command "$wordToComplete*").Name | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Add-PSProfileCommandAlias' function Get-PSProfileCommandAlias { <# .SYNOPSIS Gets an alias from $PSProfile.CommandAliases. .DESCRIPTION Gets an alias from $PSProfile.CommandAliases. .PARAMETER Alias The alias to get from $PSProfile.CommandAliases. .EXAMPLE Get-PSProfileCommandAlias -Alias code Gets the alias 'code' from $PSProfile.CommandAliases. .EXAMPLE Get-PSProfileCommandAlias Gets the list of command aliases from $PSProfile.CommandAliases. #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Alias ) Process { if ($PSBoundParameters.ContainsKey('Alias')) { Write-Verbose "Getting command alias '$Alias' from `$PSProfile.CommandAliases" $Global:PSProfile.CommandAliases.GetEnumerator() | Where-Object {$_.Key -in $Alias} } else { Write-Verbose "Getting all command aliases from `$PSProfile.CommandAliases" $Global:PSProfile.CommandAliases } } } Register-ArgumentCompleter -CommandName Get-PSProfileCommandAlias -ParameterName Alias -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.CommandAliases.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileCommandAlias' function Remove-PSProfileCommandAlias { <# .SYNOPSIS Removes an alias from $PSProfile.CommandAliases. .DESCRIPTION Removes an alias from $PSProfile.CommandAliases. .PARAMETER Alias The alias to remove from $PSProfile.CommandAliases. .PARAMETER Force If $true, also removes the alias itself from the session if it exists. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileCommandAlias -Alias code -Save Removes the alias 'code' from $PSProfile.CommandAliases then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Alias, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Alias' from `$PSProfile.CommandAliases")) { Write-Verbose "Removing '$Alias' from `$PSProfile.CommandAliases" $Global:PSProfile.CommandAliases.Remove($Alias) if ($Force -and $null -ne (Get-Alias "$Alias*")) { Write-Verbose "Removing Alias: $Alias" Remove-Item $LinkPath -Force } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileCommandAlias -ParameterName Alias -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.CommandAliases.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileCommandAlias' function Get-LastCommandDuration { <# .SYNOPSIS Gets the elapsed time of the last command via Get-History. Intended to be used in prompts. .DESCRIPTION Gets the elapsed time of the last command via Get-History. Intended to be used in prompts. .PARAMETER Id The Id of the command to get from the history. .PARAMETER Format The format string for the resulting timestamp. .EXAMPLE Get-LastCommandDuration #> [CmdletBinding()] param( [Parameter()] [int] $Id, [Parameter()] [string] $Format = "{0:h\:mm\:ss\.ffff}" ) $null = $PSBoundParameters.Remove("Format") $LastCommand = Get-History -Count 1 @PSBoundParameters if (!$LastCommand) { return "0:00:00.0000" } elseif ($null -ne $LastCommand.Duration) { $Format -f $LastCommand.Duration } else { $Duration = $LastCommand.EndExecutionTime - $LastCommand.StartExecutionTime $Format -f $Duration } } Export-ModuleMember -Function 'Get-LastCommandDuration' function Get-PathAlias { <# .SYNOPSIS Gets the Path alias using either the short name from $PSProfile.GitPathMap or a path alias stored in $PSProfile.PathAliases, falls back to using a shortened version of the root drive + current directory. .DESCRIPTION Gets the Path alias using either the short name from $PSProfile.GitPathMap or a path alias stored in $PSProfile.PathAliases, falls back to using a shortened version of the root drive + current directory. .PARAMETER Path The full path to get the PathAlias for. Defaults to $PWD.Path .PARAMETER DirectorySeparator The desired DirectorySeparator character. Defaults to $global:PathAliasDirectorySeparator if present, falls back to [System.IO.Path]::DirectorySeparatorChar if not. .EXAMPLE Get-PathAlias #> [CmdletBinding()] Param ( [parameter(Position = 0)] [string] $Path = $PWD.Path, [parameter(Position = 1)] [string] $DirectorySeparator = $(if ($null -ne $global:PathAliasDirectorySeparator) { $global:PathAliasDirectorySeparator } else { [System.IO.Path]::DirectorySeparatorChar }) ) Begin { try { $origPath = $Path if ($null -eq $global:PSProfile) { $global:PSProfile = @{ Settings = @{ PSVersionStringLength = 3 } PathAliasMap = @{ '~' = $env:USERPROFILE } } } elseif ($null -eq $global:PSProfile._internal) { $global:PSProfile._internal = @{ PathAliasMap = @{ '~' = $env:USERPROFILE } } } elseif ($null -eq $global:PSProfile._internal.PathAliasMap) { $global:PSProfile._internal.PathAliasMap = @{ '~' = $env:USERPROFILE } } if ($gitRepo = Test-IfGit) { $gitIcon = [char]0xe0a0 $key = $gitIcon + $gitRepo.Repo if (-not $global:PSProfile._internal.PathAliasMap.ContainsKey($key)) { $global:PSProfile._internal.PathAliasMap[$key] = $gitRepo.TopLevel } } $leaf = Split-Path $Path -Leaf if (-not $global:PSProfile._internal.PathAliasMap.ContainsKey('~')) { $global:PSProfile._internal.PathAliasMap['~'] = $env:USERPROFILE } Write-Verbose "Alias map => JSON: $($global:PSProfile._internal.PathAliasMap | ConvertTo-Json -Depth 5)" $aliasKey = $null $aliasValue = $null foreach ($hash in $global:PSProfile._internal.PathAliasMap.GetEnumerator() | Sort-Object { $_.Value.Length } -Descending) { if ($Path -like "$($hash.Value)*") { $Path = $Path.Replace($hash.Value,$hash.Key) $aliasKey = $hash.Key $aliasValue = $hash.Value Write-Verbose "AliasKey [$aliasKey] || AliasValue [$aliasValue]" break } } } catch { Write-Error $_ return $origPath } } Process { try { if ($null -ne $aliasKey -and $origPath -eq $aliasValue) { Write-Verbose "Matched original path! Returning alias base path" $finalPath = $Path } elseif ($null -ne $aliasKey) { Write-Verbose "Matched alias key [$aliasKey]! Returning path alias with leaf" $drive = "$($aliasKey)\" $finalPath = if ((Split-Path $origPath -Parent) -eq $aliasValue) { "$($drive)$($leaf)" } else { "$($drive)$([char]0x2026)\$($leaf)" } } else { $drive = (Get-Location).Drive.Name + ':\' Write-Verbose "Matched base drive [$drive]! Returning base path" $finalPath = if ($Path -eq $drive) { $drive } elseif ((Split-Path $Path -Parent) -eq $drive) { "$($drive)$($leaf)" } else { "$($drive)..\$($leaf)" } } if ($DirectorySeparator -notin @($null,([System.IO.Path]::DirectorySeparatorChar))) { $finalPath.Replace(([System.IO.Path]::DirectorySeparatorChar),$DirectorySeparator) } else { $finalPath } } catch { Write-Error $_ return $origPath } } } Export-ModuleMember -Function 'Get-PathAlias' function Get-PSProfileArguments { <# .SYNOPSIS Used for PSProfile Plugins to provide easy Argument Completers using PSProfile constructs. .DESCRIPTION Used for PSProfile Plugins to provide easy Argument Completers using PSProfile constructs. .PARAMETER FinalKeyOnly Returns only the final key of the completed argument to the list of completers. If $false, returns the full path. .PARAMETER WordToComplete The word to complete, typically passed in from the scriptblock arguments. .PARAMETER CommandName Here to allow passing @PSBoundParameters directly to this function from Register-ArgumentCompleter .PARAMETER ParameterName Here to allow passing @PSBoundParameters directly to this function from Register-ArgumentCompleter .PARAMETER CommandAst Here to allow passing @PSBoundParameters directly to this function from Register-ArgumentCompleter .PARAMETER FakeBoundParameter Here to allow passing @PSBoundParameters directly to this function from Register-ArgumentCompleter .EXAMPLE Get-PSProfileArguments -WordToComplete "Prompts.$wordToComplete" -FinalKeyOnly Gets the list of prompt names under the Prompts PSProfile primary key. .EXAMPLE Get-PSProfileArguments -WordToComplete "GitPathMap.$wordToComplete" -FinalKeyOnly Gets the list of Git Path short names under the GitPathMap PSProfile primary key. #> [OutputType('System.Management.Automation.CompletionResult')] [CmdletBinding()] Param( [switch] $FinalKeyOnly, [string] $WordToComplete, [object] $CommandName, [object] $ParameterName, [object] $CommandAst, [object] $FakeBoundParameter ) Process { Write-Verbose "Getting PSProfile command argument completions" $split = $WordToComplete.Split('.') $setting = $null switch ($split.Count) { 5 { $setting = $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])" $base = "$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])" } 4 { $setting = $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])" $base = "$($split[0])"."$($split[1])"."$($split[2])" } 3 { $setting = $Global:PSProfile."$($split[0])"."$($split[1])" $base = "$($split[0])"."$($split[1])" } 2 { $setting = $Global:PSProfile."$($split[0])" $base = $split[0] } } if ($null -eq $setting) { $setting = $Global:PSProfile $base = $null $final = $WordToComplete } else { $final = $split | Select-Object -Last 1 } if ($setting.GetType() -notin @([string],[int],[long],[version],[timespan],[datetime],[bool])) { $props = if ($setting.PSTypeNames -match 'Hashtable') { $setting.Keys | Where-Object {$_ -ne '_internal' -and $_ -like "$final*"} | Sort-Object } else { ($setting | Get-Member -MemberType Property,NoteProperty).Name | Where-Object {$_ -notmatch '^_' -and $_ -like "$final*"} | Sort-Object } $props | ForEach-Object { $result = if (-not $FinalKeyOnly -and $null -ne $base) { @($base,$_) -join "." } else { $_ } [System.Management.Automation.CompletionResult]::new($result, $result, 'ParameterValue', $result) } } } } Register-ArgumentCompleter -CommandName Get-PSProfileArguments -ParameterName WordToComplete -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) Get-PSProfileArguments @PSBoundParameters } Export-ModuleMember -Function 'Get-PSProfileArguments' function Get-PSVersion { <# .SYNOPSIS Gets the short formatted PSVersion string for use in a prompt or wherever else desired. .DESCRIPTION Gets the short formatted PSVersion string for use in a prompt or wherever else desired. .PARAMETER Places How many decimal places you would like the returned version string to be. Defaults to $PSProfile.Settings.PSVersionStringLength if present. .EXAMPLE Get-PSVersion -Places 2 Returns `6.2` when using PowerShell 6.2.2, or `5.1` when using Windows PowerShell 5.1.18362.10000 #> [OutputType('System.String')] [CmdletBinding()] Param ( [parameter(Position = 0)] [AllowNull()] [int] $Places = $global:PSProfile.Settings.PSVersionStringLength ) Process { $version = $PSVersionTable.PSVersion.ToString() if ($null -ne $Places) { $split = ($version -split '\.')[0..($Places - 1)] if ("$($split[-1])".Length -gt 1) { $split[-1] = "$($split[-1])".Substring(0,1) } $joined = $split -join '.' if ($version -match '[a-zA-Z]+') { $joined += "-$(($Matches[0]).Substring(0,1))" if ($version -match '\d+$') { $joined += $Matches[0] } } $joined } else { $version } } } Export-ModuleMember -Function 'Get-PSVersion' function Test-IfGit { <# .SYNOPSIS Tests if the current path is in a Git repo folder and returns the basic details as an object if so. Useful in prompts when determining current folder's Git status .DESCRIPTION Tests if the current path is in a Git repo folder and returns the basic details as an object if so. Useful in prompts when determining current folder's Git status .EXAMPLE Test-IfGit #> [CmdletBinding()] Param () Process { try { $topLevel = git rev-parse --show-toplevel *>&1 if ($topLevel -like 'fatal: *') { $Global:Error.Remove($Global:Error[0]) $false } else { $origin = git remote get-url origin $repo = Split-Path -Leaf $origin [PSCustomObject]@{ TopLevel = (Resolve-Path $topLevel).Path Origin = $origin Repo = $(if ($repo -notmatch '(\.git|\.ssh|\.tfs)$') { $repo } else { $repo.Substring(0,($repo.LastIndexOf('.'))) }) } } } catch { $false $Global:Error.Remove($Global:Error[0]) } } } Export-ModuleMember -Function 'Test-IfGit' function Write-PSProfileLog { <# .SYNOPSIS Adds a log entry to the current PSProfile Log. .DESCRIPTION Adds a log entry to the current PSProfile Log. Used for external plugins to hook into the existing log so items like Plugin load logging are contained in one place. .PARAMETER Message The message to log. .PARAMETER Section The name of the section you are logging for, e.g. the name of the plugin or overall what action is being done. .PARAMETER LogLevel The Level of the Log event. Defaults to Debug. .EXAMPLE Write-PSProfileLog -Message "Hunting for missing KBs" -Section 'KBUpdate' -LogLevel 'Verbose' #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [String] $Message, [Parameter(Mandatory,Position = 1)] [String] $Section, [Parameter(Position = 2)] [PSProfileLogLevel] $LogLevel = 'Debug' ) Process { $Global:PSProfile._log( $Message, $Section, $LogLevel ) } } Export-ModuleMember -Function 'Write-PSProfileLog' function Get-PSProfileCommand { <# .SYNOPSIS Gets the list of commands provided by PSProfile directly. .DESCRIPTION Gets the list of commands provided by PSProfile directly. .PARAMETER Command The command to get from the list of PSProfile commands. .EXAMPLE Get-PSProfileCommand Gets the full list of commands provided by PSProfile directly. #> [OutputType('System.Management.Automation.FunctionInfo')] [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Command ) Begin { $commands = Get-Command -Module PSProfile | Where-Object {$_.Name -in (Get-Module PSProfile).ExportedCommands.Keys} } Process { if ($PSBoundParameters.ContainsKey('Command')) { Write-Verbose "Getting PSProfile command '$Command'" $commands | Where-Object {$_.Name -in $Command} } else { Write-Verbose "Getting all commands provided by PSProfile directly" $commands } } } Register-ArgumentCompleter -CommandName Get-PSProfileCommand -ParameterName Command -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) (Get-Module PSProfile).ExportedCommands.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileCommand' function Get-PSProfileImportedCommand { <# .SYNOPSIS Gets the list of commands imported from scripts and plugins that are not part of PSProfile itself. .DESCRIPTION Gets the list of commands imported from scripts and plugins that are not part of PSProfile itself. .PARAMETER Command The command to get from the list of imported commands. .EXAMPLE Get-PSProfileImportedCommand Gets the full list of commands imported during PSProfile load. #> [OutputType('System.Management.Automation.FunctionInfo')] [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Command ) Begin { $commands = Get-Command -Module PSProfile | Where-Object {$_.Name -notin (Get-Module PSProfile).ExportedCommands.Keys} } Process { if ($PSBoundParameters.ContainsKey('Command')) { Write-Verbose "Getting imported command '$Command'" $commands | Where-Object {$_.Name -in $Command} } else { Write-Verbose "Getting commands imported during PSProfile load that are not part of PSProfile itself" $commands } } } Register-ArgumentCompleter -CommandName Get-PSProfileImportedCommand -ParameterName Command -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) (Get-Command -Module PSProfile | Where-Object {$_.Name -notin (Get-Module PSProfile).ExportedCommands.Keys} | Where-Object {$_ -like "$wordToComplete*"}).Name | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileImportedCommand' function Get-PSProfileLog { <# .SYNOPSIS Gets the PSProfile Log events. .DESCRIPTION Gets the PSProfile Log events. .PARAMETER Section Limit results to only a specific section. .PARAMETER LogLevel Limit results to only a specific LogLevel. .PARAMETER Summary Get a high-level summary of the PSProfile Log. .PARAMETER Raw Return the raw PSProfile Events. Returns the results via Format-Table for readability otherwise. .EXAMPLE Get-PSProfileLog Gets the current Log in full. .EXAMPLE Get-PSProfileLog -Summary Gets the Log summary. .EXAMPLE Get-PSProfileLog -Section InvokeScripts,LoadPlugins -Raw Gets the Log Events for only sections 'InvokeScripts' and 'LoadPlugins' and returns the raw Event objects. #> [CmdletBinding(DefaultParameterSetName = 'Full')] Param( [Parameter(Position = 0,ParameterSetName = 'Full')] [String[]] $Section, [Parameter(Position = 1,ParameterSetName = 'Full')] [PSProfileLogLevel[]] $LogLevel, [Parameter(ParameterSetName = 'Summary')] [Switch] $Summary, [Parameter(ParameterSetName = 'Full')] [Switch] $Raw ) Process { if ($Summary) { Write-Verbose "Getting PSProfile Log summary" $Global:PSProfile.Log | Group-Object Section | ForEach-Object { $sectName = $_.Name $Group = $_.Group $sectCaps = $Group | Where-Object {$_.Message -match '^SECTION (START|END)$'} [PSCustomObject]@{ Name = $sectName Start = $sectCaps[0].Time.ToString('HH:mm:ss.fff') SectionDuration = "$([Math]::Round(($sectCaps[-1].Time - $sectCaps[0].Time).TotalMilliseconds))ms" FullDuration = "$([Math]::Round(($Group[-1].Time - $Group[0].Time).TotalMilliseconds))ms" RunningJobs = Get-RSJob -State Running | Where-Object {$_.Name -match $sectName} | Select-Object -ExpandProperty Name } } | Sort-Object Start | Format-Table -AutoSize } else { Write-Verbose "Getting PSProfile Log" $items = if ($Section) { $Global:PSProfile.Log | Where-Object {$_.Section -in $Section} } else { $Global:PSProfile.Log } if ($LogLevel) { $items = $items | Where-Object {$_.LogLevel -in $LogLevel} } if (-not $Raw) { $items | Format-Table -AutoSize } else { $items } } } } Register-ArgumentCompleter -CommandName Get-PSProfileLog -ParameterName 'Section' -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Log.Section | Sort-Object -Unique | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileLog' function Import-PSProfile { <# .SYNOPSIS Reloads your PSProfile by running $PSProfile.Load() .DESCRIPTION Reloads your PSProfile by running $PSProfile.Load() .EXAMPLE Import-PSProfile .EXAMPLE Load-PSProfile #> [CmdletBinding()] Param() Process { Write-Verbose "Loading PSProfile configuration!" $global:PSProfile.Load() } } Export-ModuleMember -Function 'Import-PSProfile' function Import-PSProfileConfiguration { <# .SYNOPSIS Imports a Configuration.psd1 file from a specific path and overwrites differing values on the PSProfile, if any. .DESCRIPTION Imports a Configuration.psd1 file from a specific path and overwrites differing values on the PSProfile, if any. .PARAMETER Path The path to the PSD1 file you would like to import. .PARAMETER Save If $true, saves the updated PSProfile after importing. .EXAMPLE Import-PSProfileConfiguration -Path ~\MyProfile.psd1 -Save #> [CmdletBinding()] Param( [Parameter(Mandatory,Position = 0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [Alias('FullName')] [String] $Path, [Parameter()] [Switch] $Save ) Process { $Path = (Resolve-Path $Path).Path Write-Verbose "Loading PSProfile configuration from path: $Path" $Global:PSProfile._loadAdditionalConfiguration($Path) if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Import-PSProfileConfiguration' function Save-PSProfile { <# .SYNOPSIS Saves the current PSProfile configuration by calling the $PSProfile.Save() method. .DESCRIPTION Saves the current PSProfile configuration by calling the $PSProfile.Save() method. .EXAMPLE Save-PSProfile #> [CmdletBinding()] Param() Process { Write-Verbose "Saving PSProfile configuration!" $global:PSProfile.Save() } } Export-ModuleMember -Function 'Save-PSProfile' function Update-PSProfileConfig { <# .SYNOPSIS Force refreshes the current PSProfile configuration by calling the $PSProfile.Refresh() method. .DESCRIPTION Force refreshes the current PSProfile configuration by calling the $PSProfile.Refresh() method. This will update the GitPathMap with any new projects found and other tasks that don't run on every PSProfile load. .EXAMPLE Update-PSProfileConfig .EXAMPLE Refresh-PSProfile Uses the shorter alias command instead of the long command. #> [CmdletBinding()] Param() Process { Write-Verbose "Refreshing PSProfile config!" $global:PSProfile.Refresh() } } Export-ModuleMember -Function 'Update-PSProfileConfig' function Update-PSProfileRefreshFrequency { <# .SYNOPSIS Sets the Refresh Frequency for PSProfile. The $PSProfile.Refresh() runs tasks that aren't run during every profile load, i.e. SymbolicLink creation, Git project path discovery, module installation, etc. .DESCRIPTION Sets the Refresh Frequency for PSProfile. The $PSProfile.Refresh() runs tasks that aren't run during every profile load, i.e. SymbolicLink creation, Git project path discovery, module installation, etc. .PARAMETER Timespan The frequency that you would like to refresh your PSProfile configuration. Refresh will occur during the profile load after the time since last refresh has surpassed the desired refresh frequency. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Update-PSProfileRefreshFrequency -Timespan '03:00:00' -Save Updates the RefreshFrequency to 3 hours and saves the PSProfile configuration after updating. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [timespan] $Timespan, [Parameter()] [Switch] $Save ) Process { Write-Verbose "Updating PSProfile RefreshFrequency to '$($Timespan.ToString())'" $Global:PSProfile.RefreshFrequency = $Timespan.ToString() if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Update-PSProfileRefreshFrequency' function Update-PSProfileSetting { <# .SYNOPSIS Update a PSProfile property's value by tab-completing the available keys. .DESCRIPTION Update a PSProfile property's value by tab-completing the available keys. .PARAMETER Path The property path you would like to update, e.g. Settings.PSVersionStringLength .PARAMETER Value The value you would like to update for the specified setting path. .PARAMETER Add If $true, adds the value to the specified PSProfile setting value array instead of overwriting the current value. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Update-PSProfileSetting -Path Settings.PSVersionStringLength -Value 3 -Save Updates the PSVersionStringLength setting to 3 and saves the configuration. .EXAMPLE Update-PSProfileSetting -Path ScriptPaths -Value ~\ProfileLoad.ps1 -Add -Save *Adds* the 'ProfileLoad.ps1' script to the $PSProfile.ScriptPaths array of scripts to invoke during profile load, then saves the configuration. #> [CmdletBinding()] Param( [Parameter(Mandatory,Position = 0)] [String] $Path, [Parameter(Mandatory,Position = 1)] [object] $Value, [Parameter()] [switch] $Add, [Parameter()] [Switch] $Save ) Process { Write-Verbose "Updating PSProfile.$Path with value '$Value'" $split = $Path.Split('.') switch ($split.Count) { 5 { if ($Add) { $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])"."$($split[4])" += $Value } else { $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])"."$($split[4])" = $Value } } 4 { if ($Add) { $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])" += $Value } else{ $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])"."$($split[3])" = $Value } } 3 { if ($Add) { $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])" += $Value } else{ $Global:PSProfile."$($split[0])"."$($split[1])"."$($split[2])" = $Value } } 2 { if ($Add) { $Global:PSProfile."$($split[0])"."$($split[1])" += $Value } else{ $Global:PSProfile."$($split[0])"."$($split[1])" = $Value } } 1 { if ($Add) { $Global:PSProfile.$Path += $Value } else{ $Global:PSProfile.$Path = $Value } } } if ($Save) { Save-PSProfile } } } Register-ArgumentCompleter -CommandName 'Update-PSProfileSetting' -ParameterName Path -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) Get-PSProfileArguments @PSBoundParameters } Export-ModuleMember -Function 'Update-PSProfileSetting' function Add-PSProfileModuleToImport { <# .SYNOPSIS Adds a module to import during PSProfile import. .DESCRIPTION Adds a module to import during PSProfile import. .PARAMETER Name The name of the module to import. .PARAMETER Prefix Add the specified prefix to the nouns in the names of imported module members. .PARAMETER MinimumVersion Import only a version of the module that is greater than or equal to the specified value. If no version qualifies, Import-Module generates an error. .PARAMETER RequiredVersion Import only the specified version of the module. If the version is not installed, Import-Module generates an error. .PARAMETER ArgumentList Specifies arguments (parameter values) that are passed to a script module during the Import-Module command. Valid only when importing a script module. .PARAMETER Force If the module already exists in $PSProfile.ModulesToImport, use -Force to overwrite the existing value. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileModuleToImport -Name posh-git -RequiredVersion '0.7.3' -Save Specifies to import posh-git version 0.7.3 during PSProfile import then saves the updated configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Name, [Parameter()] [String] $Prefix, [Parameter()] [String] $MinimumVersion, [Parameter()] [String] $RequiredVersion, [Parameter()] [Object[]] $ArgumentList, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $Save ) Process { if (-not $Force -and $null -ne ($Global:PSProfile.ModulesToImport | Where-Object {$_ -eq [hashtable] -and $_.Name -eq $Name})) { Write-Error "Unable to add module to `$PSProfile.ModulesToImport as it already exists. Use -Force to overwrite the existing value if desired." } else { $moduleParams = $PSBoundParameters foreach ($key in $moduleParams.Keys | Where-Object {$_ -in @('Verbose','Confirm') -or $_ -notin (Get-Command Import-Module).Parameters.Keys}) { $null = $moduleParams.Remove($key) } Write-Verbose "Adding '$Name' to `$PSProfile.ModulesToImport" $Global:PSProfile.ModulesToImport += $moduleParams if ($Save) { Save-PSProfile } } } } Export-ModuleMember -Function 'Add-PSProfileModuleToImport' function Get-PSProfileModuleToImport { <# .SYNOPSIS Gets a module from $PSProfile.ModulesToImport. .DESCRIPTION Gets a module from $PSProfile.ModulesToImport. .PARAMETER Name The name of the module to get from $PSProfile.ModulesToImport. .EXAMPLE Get-PSProfileModuleToImport -Name posh-git Gets posh-git from $PSProfile.ModulesToImport .EXAMPLE Get-PSProfileModuleToImport Gets the list of modules to import from $PSProfile.ModulesToImport #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Name ) Process { if ($PSBoundParameters.ContainsKey('Name')) { Write-Verbose "Getting ModuleToImport '$Name' from `$PSProfile.ModulesToImport" $Global:PSProfile.ModulesToImport | Where-Object {$_ -in $Name -or $_.Name -in $Name} } else { Write-Verbose "Getting all command aliases from `$PSProfile.ModulesToImport" $Global:PSProfile.ModulesToImport } } } Register-ArgumentCompleter -CommandName Get-PSProfileModuleToImport -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ModulesToImport | ForEach-Object { if ($_ -is [hashtable]) { $_.Name } else { $_ } } | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileModuleToImport' function Remove-PSProfileModuleToImport { <# .SYNOPSIS Removes a module from $PSProfile.ModulesToImport. .DESCRIPTION Removes a module from $PSProfile.ModulesToImport. .PARAMETER Name The name of the module to remove from $PSProfile.ModulesToImport. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileModuleToImport -Name posh-git -Save Removes posh-git from $PSProfile.ModulesToImport then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Name' from `$PSProfile.ModulesToImport")) { Write-Verbose "Removing '$Name' from `$PSProfile.ModulesToImport" $Global:PSProfile.ModulesToImport = $Global:PSProfile.ModulesToImport | Where-Object {($_ -is [hashtable] -and $_.Name -ne $Name) -or ($_ -is [string] -and $_ -ne $Name)} if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileModuleToImport -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ModulesToImport | ForEach-Object { if ($_ -is [hashtable]) { $_.Name } else { $_ } } | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileModuleToImport' function Add-PSProfileModuleToInstall { <# .SYNOPSIS Adds a module to ensure is installed in the CurrentUser scope. Module installations are handled via background job during PSProfile import. .DESCRIPTION Adds a module to ensure is installed in the CurrentUser scope. Module installations are handled via background job during PSProfile import. .PARAMETER Name The name of the module to install. .PARAMETER Repository The repository to install the module from. Defaults to the PowerShell Gallery. .PARAMETER MinimumVersion The minimum version of the module to install. .PARAMETER RequiredVersion The required version of the module to install. .PARAMETER AcceptLicense If $true, accepts the license for the module if necessary. .PARAMETER AllowPrerelease If $true, allows installation of prerelease versions of the module. .PARAMETER Force If the module already exists in $PSProfile.ModulesToInstall, use -Force to overwrite the existing value. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileModuleToInstall -Name posh-git -RequiredVersion '0.7.3' -Save Specifies to install posh-git version 0.7.3 during PSProfile import if missing then saves the updated configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Name, [Parameter()] [String] $Repository, [Parameter()] [String] $MinimumVersion, [Parameter()] [String] $RequiredVersion, [Parameter()] [Switch] $AcceptLicense, [Parameter()] [Switch] $AllowPrerelease, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $Save ) Process { if (-not $Force -and $null -ne ($Global:PSProfile.ModulesToInstall | Where-Object {$_ -eq [hashtable] -and $_.Name -eq $Name})) { Write-Error "Unable to add module to `$PSProfile.ModulesToInstall as it already exists. Use -Force to overwrite the existing value if desired." } else { $moduleParams = $PSBoundParameters foreach ($key in $moduleParams.Keys | Where-Object {$_ -notin @('Verbose','Confirm',((Get-Command Install-Module).Parameters.Keys))}) { $moduleParams.Remove($key) } Write-Verbose "Adding '$Name' to `$PSProfile.ModulesToInstall" $Global:PSProfile.ModulesToInstall += $moduleParams } } } Export-ModuleMember -Function 'Add-PSProfileModuleToInstall' function Get-PSProfileModuleToInstall { <# .SYNOPSIS Gets a module from $PSProfile.ModulesToInstall. .DESCRIPTION Gets a module from $PSProfile.ModulesToInstall. .PARAMETER Name The name of the module to get from $PSProfile.ModulesToInstall. .EXAMPLE Get-PSProfileModuleToInstall -Name posh-git Gets posh-git from $PSProfile.ModulesToInstall .EXAMPLE Get-PSProfileModuleToInstall Gets the list of modules to install from $PSProfile.ModulesToInstall #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Name ) Process { if ($PSBoundParameters.ContainsKey('Name')) { Write-Verbose "Getting ModuleToImport '$Name' from `$PSProfile.ModulesToInstall" $Global:PSProfile.ModulesToInstall | Where-Object {$_ -in $Name -or $_.Name -in $Name} } else { Write-Verbose "Getting all command aliases from `$PSProfile.ModulesToInstall" $Global:PSProfile.ModulesToInstall } } } Register-ArgumentCompleter -CommandName Get-PSProfileModuleToInstall -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ModulesToInstall | ForEach-Object { if ($_ -is [hashtable]) { $_.Name } else { $_ } } | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileModuleToInstall' function Remove-PSProfileModuleToInstall { <# .SYNOPSIS Removes a module from $PSProfile.ModulesToInstall. .DESCRIPTION Removes a module from $PSProfile.ModulesToInstall. .PARAMETER Name The name of the module to remove from $PSProfile.ModulesToInstall. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileModuleToInstall -Name posh-git -Save Removes posh-git from $PSProfile.ModulesToInstall then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Name' from `$PSProfile.ModulesToInstall")) { Write-Verbose "Removing '$Name' from `$PSProfile.ModulesToInstall" $Global:PSProfile.ModulesToInstall = $Global:PSProfile.ModulesToInstall | Where-Object {($_ -is [hashtable] -and $_.Name -ne $Name) -or ($_ -is [string] -and $_ -ne $Name)} if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileModuleToInstall -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ModulesToInstall | ForEach-Object { if ($_ -is [hashtable]) { $_.Name } else { $_ } } | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileModuleToInstall' function Add-PSProfilePathAlias { <# .SYNOPSIS Adds a path alias to your PSProfile configuration. Path aliases are used for path shortening in prompts via Get-PathAlias. .DESCRIPTION Adds a path alias to your PSProfile configuration. Path aliases are used for path shortening in prompts via Get-PathAlias. .PARAMETER Alias The alias to substitute the full path for in prompts via Get-PathAlias. .PARAMETER Path The full path to be substituted. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfilePathAlias -Alias ~ -Path $env:USERPROFILE -Save Adds a path alias of ~ for the current UserProfile folder and saves your PSProfile configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [String] $Alias, [Parameter(Mandatory,Position = 1)] [String] $Path, [Parameter()] [Switch] $Save ) Process { Write-Verbose "Adding alias '$Alias' to path '$Path' to PSProfile" $Global:PSProfile.PathAliases[$Alias] = $Path if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfilePathAlias' function Get-PSProfilePathAlias { <# .SYNOPSIS Gets a module from $PSProfile.PathAliases. .DESCRIPTION Gets a module from $PSProfile.PathAliases. .PARAMETER Alias The Alias to get from $PSProfile.PathAliases. .EXAMPLE Get-PSProfilePathAlias -Alias ~ Gets the alias '~' from $PSProfile.PathAliases .EXAMPLE Get-PSProfilePathAlias Gets the list of path aliases from $PSProfile.PathAliases #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Alias ) Process { if ($PSBoundParameters.ContainsKey('Alias')) { Write-Verbose "Getting Path Alias '$Alias' from `$PSProfile.PathAliases" $Global:PSProfile.PathAliases.GetEnumerator() | Where-Object {$_.Key -in $Alias} } else { Write-Verbose "Getting all command aliases from `$PSProfile.PathAliases" $Global:PSProfile.PathAliases } } } Register-ArgumentCompleter -CommandName Get-PSProfilePathAlias -ParameterName Alias -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.PathAliases.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfilePathAlias' function Remove-PSProfilePathAlias { <# .SYNOPSIS Removes an alias from $PSProfile.PathAliases. .DESCRIPTION Removes an alias from $PSProfile.PathAliases. .PARAMETER Alias The alias to remove from $PSProfile.PathAliases. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfilePathAlias -Alias Workplace -Save Removes the alias 'Workplace' from $PSProfile.PathAliases then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Alias, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Alias' from `$PSProfile.PathAliases")) { Write-Verbose "Removing '$Alias' from `$PSProfile.PathAliases" $Global:PSProfile.PathAliases.Remove($Alias) if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfilePathAlias -ParameterName Alias -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.PathAliases.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfilePathAlias' function Add-PSProfilePlugin { <# .SYNOPSIS Adds a PSProfile Plugin to the list of plugins. If the plugin already exists, it will overwrite it. Re-imports your PSProfile once done to load any newly added plugins. .DESCRIPTION Adds a PSProfile Plugin to the list of plugins. If the plugin already exists, it will overwrite it. Re-imports your PSProfile once done to load any newly added plugins. .PARAMETER Name The name of the Plugin to add, e.g. 'PSProfile.PowerTools' .PARAMETER ArgumentList Any arguments that need to be passed to the plugin on import, such as a hashtable to process. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfilePlugin -Name 'PSProfile.PowerTools' -Save Adds the included plugin 'PSProfile.PowerTools' to your PSProfile and saves it so it persists. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [String[]] $Name, [Parameter(Position = 1)] [Object] $ArgumentList, [Parameter()] [Switch] $Save ) Process { foreach ($pName in $Name) { Write-Verbose "Adding plugin '$pName' to `$PSProfile.Plugins" $plugin = @{ Name = $pName } if ($PSBoundParameters.ContainsKey('ArgumentList')) { $plugin['ArgumentList'] = $ArgumentList } $temp = $Global:PSProfile.Plugins | Where-Object {$_.Name -ne $pName} $temp += $plugin $Global:PSProfile.Plugins = $temp } if ($Save) { Save-PSProfile } Import-PSProfile -Verbose:$false } } Export-ModuleMember -Function 'Add-PSProfilePlugin' function Get-PSProfilePlugin { <# .SYNOPSIS Gets a Plugin from $PSProfile.Plugins. .DESCRIPTION Gets a Plugin from $PSProfile.Plugins. .PARAMETER Name The name of the Plugin to get from $PSProfile.Plugins. .EXAMPLE Get-PSProfilePlugin -Name PSProfile.Prompt Gets PSProfile.Prompt from $PSProfile.Plugins .EXAMPLE Get-PSProfilePlugin Gets the list of Plugins from $PSProfile.Plugins #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Name ) Process { if ($PSBoundParameters.ContainsKey('Name')) { Write-Verbose "Getting Plugin '$Name' from `$PSProfile.Plugins" $Global:PSProfile.Plugins | Where-Object {$_.Name -in $Name} } else { Write-Verbose "Getting all Plugins from `$PSProfile.Plugins" $Global:PSProfile.Plugins } } } Register-ArgumentCompleter -CommandName Get-PSProfilePlugin -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Plugins | ForEach-Object {$_.Name} | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfilePlugin' function Remove-PSProfilePlugin { <# .SYNOPSIS Removes a PSProfile Plugin from $PSProfile.Plugins. .DESCRIPTION Removes a PSProfile Plugin from $PSProfile.Plugins. .PARAMETER Name The name of the Plugin to remove from $PSProfile.Plugins. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfilePlugin -Name 'PSProfile.PowerTools' -Save Removes the Plugin 'PSProfile.PowerTools' from $PSProfile.Plugins then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Name' from `$PSProfile.Plugins")) { Write-Verbose "Removing '$Name' from `$PSProfile.Plugins" $Global:PSProfile.Plugins = $Global:PSProfile.Plugins | Where-Object {$_.Name -ne $Name} if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfilePlugin -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Plugins.Name | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfilePlugin' function Add-PSProfileProjectPath { <# .SYNOPSIS Adds a ProjectPath to your PSProfile to find Git project folders under during PSProfile refresh. These will be available via tab-completion .DESCRIPTION Adds a ProjectPath to your PSProfile to find Git project folders under during PSProfile refresh. .PARAMETER Path The path of the folder to add to your $PSProfile.ProjectPaths. This path should contain Git repo folders underneath it. .PARAMETER NoRefresh If $true, skips refreshing your PSProfile after updating project paths. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileProjectPath -Path ~\GitRepos -Save Adds the folder ~\GitRepos to $PSProfile.ProjectPaths and saves the configuration after updating. .EXAMPLE Add-PSProfileProjectPath C:\Git -Verbose Adds the path C:\Git to your $PSProfile.ProjectPaths, refreshes your PathDict but does not save. Call Save-PSProfile after if satisfied with the results. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [ValidateScript({if (-not (Get-Item $_).PSIsContainer){throw "$_ is not a folder! Please add only folders to this PSProfile property. If you would like to add a script, use Add-PSProfileScriptPath instead."}})] [Alias('FullName')] [String[]] $Path, [Parameter()] [Switch] $NoRefresh, [Parameter()] [Switch] $Save ) Process { foreach ($p in $Path) { $fP = (Resolve-Path $p).Path if ($Global:PSProfile.ProjectPaths -notcontains $fP) { Write-Verbose "Adding ProjectPath to PSProfile: $fP" $Global:PSProfile.ProjectPaths += $fP } else { Write-Verbose "ProjectPath already in PSProfile: $fP" } } if (-not $NoRefresh) { Update-PSProfileConfig } if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfileProjectPath' function Get-PSProfileProjectPath { <# .SYNOPSIS Gets a project path from $PSProfile.ProjectPaths. .DESCRIPTION Gets a project path from $PSProfile.ProjectPaths. .PARAMETER Path The project path to get from $PSProfile.ProjectPaths. .EXAMPLE Get-PSProfileProjectPath -Path E:\Git Gets the path 'E:\Git' from $PSProfile.ProjectPaths .EXAMPLE Get-PSProfileProjectPath Gets the list of project paths from $PSProfile.ProjectPaths #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Path ) Process { if ($PSBoundParameters.ContainsKey('Path')) { Write-Verbose "Getting project path '$Path' from `$PSProfile.ProjectPaths" $Global:PSProfile.ProjectPaths | Where-Object {$_ -in $Path} } else { Write-Verbose "Getting all project paths from `$PSProfile.ProjectPaths" $Global:PSProfile.ProjectPaths } } } Register-ArgumentCompleter -CommandName Get-PSProfileProjectPath -ParameterName Path -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ProjectPaths | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileProjectPath' function Remove-PSProfileProjectPath { <# .SYNOPSIS Removes a Project Path from $PSProfile.ProjectPaths. .DESCRIPTION Removes a Project Path from $PSProfile.ProjectPaths. .PARAMETER Path The path to remove from $PSProfile.ProjectPaths. .PARAMETER NoRefresh If $true, skips refreshing your PSProfile after updating project paths. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileProjectPath -Name E:\Git -Save Removes the path 'E:\Git' from $PSProfile.ProjectPaths then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Path, [Parameter()] [Switch] $NoRefresh, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Path' from `$PSProfile.ProjectPaths")) { Write-Verbose "Removing '$Path' from `$PSProfile.ProjectPaths" $Global:PSProfile.ProjectPaths = $Global:PSProfile.ProjectPaths | Where-Object {$_ -notin @($Path,(Resolve-Path $Path).Path)} if (-not $NoRefresh) { Update-PSProfileConfig } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileProjectPath -ParameterName Path -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ProjectPaths | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileProjectPath' function Add-PSProfilePrompt { <# .SYNOPSIS Saves the Content to $PSProfile.Prompts as the Name provided for recall later. .DESCRIPTION Saves the Content to $PSProfile.Prompts as the Name provided for recall later. .PARAMETER Name The Name to save the prompt as. .PARAMETER Content The prompt content itself. .PARAMETER SetAsDefault If $true, sets the prompt as default by updated $PSProfile.Settings.DefaultPrompt. .EXAMPLE Add-PSProfilePrompt -Name Demo -Content '"PS > "' Saves a prompt named 'Demo' with the provided content. #> [CmdletBinding()] Param( [Parameter(Position = 0)] [String] $Name = $global:PSProfile.Settings.DefaultPrompt, [Parameter()] [object] $Content, [Parameter()] [switch] $SetAsDefault ) Process { if ($null -eq $Name) { throw "No value set for the Name parameter or resolved from PSProfile!" } else { Write-Verbose "Saving prompt '$Name' to `$PSProfile.Prompts" $tempContent = if ($Content) { $Content.ToString() } else { Get-PSProfilePrompt -Raw } $cleanContent = (($tempContent -split "[\r\n]" | Where-Object {$_}) -join "`n").Trim() $global:PSProfile.Prompts[$Name] = $cleanContent if ($SetAsDefault) { $global:PSProfile.Settings.DefaultPrompt = $Name } Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfilePrompt' function Edit-PSProfilePrompt { <# .SYNOPSIS Enables editing the prompt from the desired editor. Once temporary file is saved, the prompt is updated in $PSProfile.Prompts. .DESCRIPTION Enables editing the prompt from the desired editor. Once temporary file is saved, the prompt is updated in $PSProfile.Prompts. .PARAMETER Temporary If $true, does not save the PSProfile after updating the prompt. .EXAMPLE Edit-PSProfilePrompt Opens the current prompt as a temporary file in Visual Studio Code to edit. Once the file is saved and closed, the prompt is updated with the changes and saved back to $PSProfile. #> [CmdletBinding()] Param( [Parameter()] [Switch] $Temporary ) Process { $in = @{ StdIn = Get-PSProfilePrompt -Global TmpFile = [System.IO.Path]::Combine(([System.IO.Path]::GetTempPath()),"ps-prompt-$(-join ((97..(97+25)|%{[char]$_}) | Get-Random -Count 3)).ps1") } $handler = { Param( [hashtable] $in ) try { $code = (Get-Command code -All | Where-Object { $_.CommandType -notin @('Function','Alias') })[0].Source $in.StdIn | Set-Content $in.TmpFile -Force & $code $in.TmpFile --wait } catch { throw } finally { if (Test-Path $in.TmpFile -ErrorAction SilentlyContinue) { Invoke-Expression ([System.IO.File]::ReadAllText($in.TmpFile)) Remove-Item $in.TmpFile -Force } } } Write-Verbose "Opening prompt in VS Code" .$handler($in) if (-not $Temporary) { Save-PSProfilePrompt } } } Export-ModuleMember -Function 'Edit-PSProfilePrompt' function Get-PSProfilePrompt { <# .SYNOPSIS Gets the current prompt's definition as a string. Useful for inspection of the prompt in use. If PSScriptAnalyzer is installed, formats the prompt for readability before returning the prompt function string. .DESCRIPTION Gets the current prompt's definition as a string. Useful for inspection of the prompt in use. If PSScriptAnalyzer is installed, formats the prompt for readability before returning the prompt function string. .PARAMETER Name The Name of the prompt from $PSProfile.Prompts to get. If excluded, gets the current prompt. .PARAMETER Global If $true, adds the global scope to the returned prompt, e.g. `function global:prompt` .PARAMETER Raw If $true, returns only the prompt definition and does not add the `function prompt {...}` enclosure. .EXAMPLE Get-PSProfilePrompt #> [CmdletBinding()] Param( [Parameter(Position = 0)] [String] $Name, [Parameter()] [Switch] $Global, [Parameter()] [Switch] $Raw ) Begin { $pssa = if ($null -eq (Get-Module PSScriptAnalyzer* -ListAvailable)) { $false } else { $true Import-Module PSScriptAnalyzer } $pContents = if ($PSBoundParameters.ContainsKey('Name')) { $Global:PSProfile.Prompts[$Name] } else { (Get-Command prompt).Definition } } Process { Write-Verbose "Getting current prompt" $i = 0 $leadingWhiteSpace = $null $g = if ($Global) { 'global:prompt' } else { 'prompt' } $p = $(if ($Raw) { '' } else { "function $g {`n" }) + $($pContents -split "`n" | ForEach-Object { if (-not [String]::IsNullOrWhiteSpace($_)) { if ($null -eq $leadingWhiteSpace) { $lws = ($_ | Select-String -Pattern '^\s+') $leadingWhiteSpace = if ($lws) { $lws.Matches[0].Value + ' ' } else { $null } } $_ -replace "^$leadingWhiteSpace",' ' "`n" } elseif ($i) { $_ "`n" } $i++ }) + $(if ($Raw) { '' } else { "}" }) if ($pssa) { Invoke-Formatter $p -Verbose:$false } else { $p } } } Register-ArgumentCompleter -CommandName Get-PSProfilePrompt -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) Get-PSProfileArguments -WordToComplete "Prompts.$wordToComplete" -FinalKeyOnly } Export-ModuleMember -Function 'Get-PSProfilePrompt' function Remove-PSProfilePrompt { <# .SYNOPSIS Removes a Prompt from $PSProfile.Prompts. .DESCRIPTION Removes a Prompt from $PSProfile.Prompts. .PARAMETER Name The name of the prompt to remove from $PSProfile.Prompts. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfilePrompt -Name Demo -Save Removes the Prompt named 'Demo' from $PSProfile.Prompts then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0)] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing prompt '$Name' from `$PSProfile.Prompts")) { Write-Verbose "Removing prompt '$Name' from `$PSProfile.Prompts" if ($Global:PSProfile.Prompts.ContainsKey($Name)) { $Global:PSProfile.Prompts.Remove($Name) } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfilePrompt -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Prompts.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfilePrompt' function Switch-PSProfilePrompt { <# .SYNOPSIS Sets the prompt to the desired prompt by either the Name of the prompt as stored in $PSProfile.Prompts or the provided prompt content. .DESCRIPTION Sets the prompt to the desired prompt by either the Name of the prompt as stored in $PSProfile.Prompts or the provided prompt content. .PARAMETER Name The Name of the prompt to set as active from $PSProfile.Prompts. .PARAMETER Temporary If $true, does not update $PSProfile.Settings.DefaultPrompt with the selected prompt so that prompt selection does not persist after the current session. .PARAMETER Content If Content is provided as either a ScriptBlock or String, sets the current prompt to that. Equivalent to passing `function prompt {$Content}` .EXAMPLE Switch-PSProfilePrompt -Name Demo Sets the active prompt to the prompt named 'Demo' from $PSProfile.Prompts and saves it as the Default prompt for session persistence. #> [CmdletBinding(DefaultParameterSetName = 'Name')] Param( [Parameter(Mandatory,Position = 0,ParameterSetName = 'Name')] [String] $Name, [Parameter(ParameterSetName = 'Name')] [switch] $Temporary, [Parameter(Mandatory,ParameterSetName = 'Content')] [object] $Content ) Process { switch ($PSCmdlet.ParameterSetName) { Name { if ($global:PSProfile.Prompts.ContainsKey($Name)) { Write-Verbose "Setting active prompt to '$Name'" $function:prompt = $global:PSProfile.Prompts[$Name] if (-not $Temporary) { $global:PSProfile.Settings.DefaultPrompt = $Name Save-PSProfile } } else { Write-Warning "Falling back to default prompt -- '$Name' not found in Configuration prompts!" $function:prompt = ' "PS $($executionContext.SessionState.Path.CurrentLocation)$(''>'' * ($nestedPromptLevel + 1)) "; # .Link # https://go.microsoft.com/fwlink/?LinkID=225750 # .ExternalHelp System.Management.Automation.dll-help.xml ' } } Content { Write-Verbose "Setting active prompt to provided content directly" $function:prompt = $Content } } } } Register-ArgumentCompleter -CommandName Switch-PSProfilePrompt -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) Get-PSProfileArguments -WordToComplete "Prompts.$wordToComplete" -FinalKeyOnly } Export-ModuleMember -Function 'Switch-PSProfilePrompt' function Add-PSProfileScriptPath { <# .SYNOPSIS Adds a ScriptPath to your PSProfile to invoke during profile load. .DESCRIPTION Adds a ScriptPath to your PSProfile to invoke during profile load. .PARAMETER Path The path of the script to add to your $PSProfile.ScriptPaths. .PARAMETER Invoke If $true, invokes the script path after adding to $PSProfile.ScriptPaths to make it immediately available in the current session. .PARAMETER Invoke If $true, invokes the script at the path specified to load it into the current session. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileScriptPath -Path ~\MyProfileScript.ps1 -Save Adds the script 'MyProfileScript.ps1' to $PSProfile.ScriptPaths and saves the configuration after updating. .EXAMPLE Get-ChildItem .\MyProfileScripts -Recurse -File | Add-PSProfileScriptPath -Verbose Adds all scripts under the MyProfileScripts folder to $PSProfile.ScriptPaths but does not save to allow inspection. Call Save-PSProfile after to save the results if satisfied. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [Alias('FullName')] [String[]] $Path, [Parameter()] [Switch] $Invoke, [Parameter()] [Switch] $Save ) Process { foreach ($p in $Path) { if ($p -match '\.ps1$') { $fP = (Resolve-Path $p).Path if ($Global:PSProfile.ScriptPaths -notcontains $fP) { Write-Verbose "Adding ScriptPath to PSProfile: $fP" $Global:PSProfile.ScriptPaths += $fP } else { Write-Verbose "ScriptPath already in PSProfile: $fP" } if ($Invoke) { . $fp } } else { Write-Verbose "Skipping non-ps1 file: $fP" } } if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfileScriptPath' function Get-PSProfileScriptPath { <# .SYNOPSIS Gets a script path from $PSProfile.ScriptPaths. .DESCRIPTION Gets a script path from $PSProfile.ScriptPaths. .PARAMETER Path The script path to get from $PSProfile.ScriptPaths. .EXAMPLE Get-PSProfileScriptPath -Path E:\Git\MyProfileScript.ps1 Gets the path 'E:\Git\MyProfileScript.ps1' from $PSProfile.ScriptPaths .EXAMPLE Get-PSProfileScriptPath Gets the list of script paths from $PSProfile.ScriptPaths #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $Path ) Process { if ($PSBoundParameters.ContainsKey('Path')) { Write-Verbose "Getting script path '$Path' from `$PSProfile.ScriptPaths" $Global:PSProfile.ScriptPaths | Where-Object {$_ -in $Path} } else { Write-Verbose "Getting all script paths from `$PSProfile.ScriptPaths" $Global:PSProfile.ScriptPaths } } } Register-ArgumentCompleter -CommandName Get-PSProfileScriptPath -ParameterName Path -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ScriptPaths | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileScriptPath' function Remove-PSProfileScriptPath { <# .SYNOPSIS Removes a Script Path from $PSProfile.ScriptPaths. .DESCRIPTION Removes a Script Path from $PSProfile.ScriptPaths. .PARAMETER Path The path to remove from $PSProfile.ScriptPaths. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileScriptPath -Name ~\Scripts\ProfileLoadScript.ps1 -Save Removes the path '~\Scripts\ProfileLoadScript.ps1' from $PSProfile.ScriptPaths then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0,ValueFromPipeline)] [String] $Path, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Path' from `$PSProfile.ScriptPaths")) { Write-Verbose "Removing '$Path' from `$PSProfile.ScriptPaths" $Global:PSProfile.ScriptPaths = $Global:PSProfile.ScriptPaths | Where-Object {$_ -notin @($Path,(Resolve-Path $Path).Path)} if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileScriptPath -ParameterName Path -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.ScriptPaths | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileScriptPath' function Add-PSProfileSecret { <# .SYNOPSIS Adds a PSCredential object or named SecureString to the PSProfile Vault then saves the current PSProfile. .DESCRIPTION Adds a PSCredential object or named SecureString to the PSProfile Vault then saves the current PSProfile. .PARAMETER Credential The PSCredential to add to the Vault. PSCredentials are recallable by the UserName from the stored PSCredential object via either `Get-MyCreds` or `Get-PSProfileSecret -UserName $UserName`. .PARAMETER Name For SecureString secrets, the friendly name to store them as for easy recall later via `Get-PSProfileSecret`. .PARAMETER SecureString The SecureString to store as the provided Name for recall later. .PARAMETER Force If $true and the PSCredential's UserName or SecureString's Name already exists, it overwrites it. Defaults to $false to prevent accidentally overwriting existing secrets in the $PSProfile.Vault. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileSecret (Get-Credential) -Save Opens a Get-Credential window or prompt to enable entering credentials securely, then stores it in the Vault and saves your PSProfile configuration after updating. .EXAMPLE Add-PSProfileSecret -Name HomeApiKey -Value (ConvertTo-SecureString 1234567890xxx -AsPlainText -Force) -Save Stores the secret value '1234567890xxx' as the name 'HomeApiKey' in $PSProfile.Vault and saves your PSProfile configuration after updating. #> [CmdletBinding(DefaultParameterSetName = "PSCredential")] Param ( [Parameter(Mandatory,ValueFromPipeline,Position = 0,ParameterSetName = "PSCredential")] [pscredential] $Credential, [Parameter(Mandatory,ParameterSetName = "SecureString")] [string] $Name, [Parameter(Mandatory,ParameterSetName = "SecureString")] [securestring] $SecureString, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $Save ) Process { switch ($PSCmdlet.ParameterSetName) { PSCredential { if ($Force -or $null -eq $Global:PSProfile.Vault.GetSecret($Credential.UserName)) { Write-Verbose "Adding PSCredential for user '$($Credential.UserName)' to `$PSProfile.Vault" $Global:PSProfile.Vault.SetSecret($Credential) } elseif (-not $Force -and $null -ne $Global:PSProfile.Vault.GetSecret($Credential.UserName)) { Write-Error "A secret with the name '$($Credential.UserName)' already exists! Include -Force to overwrite it." } } SecureString { if ($Force -or $null -eq $Global:PSProfile.Vault.GetSecret($Name)) { Write-Verbose "Adding SecureString secret with name '$Name' to `$PSProfile.Vault" $Global:PSProfile.Vault.SetSecret($Name,$SecureString) } elseif (-not $Force -and $null -ne $Global:PSProfile.Vault.GetSecret($Name)) { Write-Error "A secret with the name '$Name' already exists! Include -Force to overwrite it." } } } if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfileSecret' function Get-MyCreds { <# .SYNOPSIS Gets a credential object from the PSProfile Vault. Defaults to getting your current user's PSCredentials if stored in the Vault. .DESCRIPTION Gets a credential object from the PSProfile Vault. Defaults to getting your current user's PSCredentials if stored in the Vault. .PARAMETER Item The name of the Secret you would like to retrieve from the Vault. .PARAMETER IncludeDomain If $true, prepends the domain found in $env:USERDOMAIN to the Username on the PSCredential object before returning it. If not currently in a domain, prepends the MachineName instead. .EXAMPLE Get-MyCreds Gets the current user's PSCredentials from the Vault. .EXAMPLE Invoke-Command -ComputerName Server01 -Credential (Creds) Passes your current user credentials via the `Creds` alias to the Credential parameter of Invoke-Command to make a call against Server01 using your PSCredential .EXAMPLE Invoke-Command -ComputerName Server01 -Credential (Get-MyCreds SvcAcct07) Passes the credentials for account SvcAcct07 to the Credential parameter of Invoke-Command to make a call against Server01 using a different PSCredential than your own. #> [OutputType('PSCredential')] [CmdletBinding()] Param( [parameter(Mandatory = $false,Position = 0)] [String] $Item = $(if ($env:USERNAME) { $env:USERNAME } elseif ($env:USER) { $env:USER }), [parameter(Mandatory = $false)] [Alias('d','Domain')] [Switch] $IncludeDomain ) Process { if ($Item) { Write-Verbose "Checking Credential Vault for user '$Item'" if ($creds = $global:PSProfile.Vault.GetSecret($Item)) { Write-Verbose "Found item in CredStore" if (!$env:USERDOMAIN) { $env:USERDOMAIN = [System.Environment]::MachineName } if ($IncludeDomain -and $creds.UserName -notlike "$($env:USERDOMAIN)\*") { $creds = New-Object PSCredential "$($env:USERDOMAIN)\$($creds.UserName)",$creds.Password } return $creds } else { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.Management.Automation.ItemNotFoundException]"Could not find secret item '$Item' in the PSProfileVault"), 'PSProfile.Vault.SecretNotFound', [System.Management.Automation.ErrorCategory]::InvalidArgument, $global:PSProfile ) ) } } else { $global:PSProfile.Vault._secrets } } } Register-ArgumentCompleter -CommandName Get-MyCreds -ParameterName Item -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Vault._secrets.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-MyCreds' function Get-PSProfileSecret { <# .SYNOPSIS Gets a Secret from the $PSProfile.Vault. .DESCRIPTION Gets a Secret from the $PSProfile.Vault. .PARAMETER Name The name of the Secret you would like to retrieve from the Vault. .EXAMPLE Get-PSProfileSecret -Name MyApiKey Gets the Secret named 'MyApiKey' from the $PSProfile.Vault. #> [CmdletBinding()] Param( [parameter(Mandatory,Position = 0)] [String] $Name ) Process { Write-Verbose "Getting Secret '$Name' from `$PSProfile.Vault" $global:PSProfile.Vault.GetSecret($Name) } } Register-ArgumentCompleter -CommandName Get-PSProfileSecret -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Vault._secrets.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileSecret' function Remove-PSProfileSecret { <# .SYNOPSIS Removes a Secret from $PSProfile.Vault. .DESCRIPTION Removes a Secret from $PSProfile.Vault. .PARAMETER Name The Secret's Name or UserName to remove from the Vault. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileSecret -Name $env:USERNAME -Save Removes the current user's stored credentials from the $PSProfile.Vault, then saves the configuration after updating. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0)] [Alias('UserName')] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$Name' from `$PSProfile.Vault")) { if ($Global:PSProfile.Vault._secrets.ContainsKey($Name)) { Write-Verbose "Removing '$Name' from `$PSProfile.Vault" $Global:PSProfile.Vault.RemoveSecret($Name) } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileSecret -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Vault._secrets.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileSecret' function Add-PSProfileSymbolicLink { <# .SYNOPSIS Adds a SymbolicLink to set if missing during profile load via background task. .DESCRIPTION Adds a SymbolicLink to set if missing during profile load via background task. .PARAMETER LinkPath The path of the symbolic link to create if missing. .PARAMETER ActualPath The actual target path of the symbolic link to set. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileSymbolicLink -LinkPath C:\workstation -ActualPath E:\Git\workstation -Save Adds a symbolic link at path 'C:\workstation' targeting the actual path 'E:\Git\workstation' and saves your PSProfile configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [Alias('Path','Name')] [String] $LinkPath, [Parameter(Mandatory,Position = 1)] [Alias('Target','Value')] [String] $ActualPath, [Parameter()] [Switch] $Save ) Process { Write-Verbose "Adding SymbolicLink '$LinkPath' pointing at ActualPath '$ActualPath'" $Global:PSProfile.SymbolicLinks[$LinkPath] = $ActualPath if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfileSymbolicLink' function Get-PSProfileSymbolicLink { <# .SYNOPSIS Gets a module from $PSProfile.SymbolicLinks. .DESCRIPTION Gets a module from $PSProfile.SymbolicLinks. .PARAMETER LinkPath The LinkPath to get from $PSProfile.SymbolicLinks. .EXAMPLE Get-PSProfileSymbolicLink -LinkPath C:\workstation Gets the LinkPath 'C:\workstation' from $PSProfile.SymbolicLinks .EXAMPLE Get-PSProfileSymbolicLink Gets the list of LinkPaths from $PSProfile.SymbolicLinks #> [CmdletBinding()] Param ( [Parameter(Position = 0,ValueFromPipeline)] [String[]] $LinkPath ) Process { if ($PSBoundParameters.ContainsKey('LinkPath')) { Write-Verbose "Getting Path LinkPath '$LinkPath' from `$PSProfile.SymbolicLinks" $Global:PSProfile.SymbolicLinks.GetEnumerator() | Where-Object {$_.Key -in $LinkPath} } else { Write-Verbose "Getting all command aliases from `$PSProfile.SymbolicLinks" $Global:PSProfile.SymbolicLinks } } } Register-ArgumentCompleter -CommandName Get-PSProfileSymbolicLink -ParameterName LinkPath -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.SymbolicLinks.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileSymbolicLink' function Remove-PSProfileSymbolicLink { <# .SYNOPSIS Removes a Symbolic Link from $PSProfile.SymbolicLinks. .DESCRIPTION Removes a PSProfile Plugin from $PSProfile.SymbolicLinks. .PARAMETER LinkPath The path of the symbolic link to remove from $PSProfile.SymbolicLinks. .PARAMETER Force If $true, also removes the SymbolicLink itself from the OS if it exists. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileSymbolicLink -LinkPath 'C:\workstation' -Force -Save Removes the SymbolicLink 'C:\workstation' from $PSProfile.SymbolicLinks, removes the then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory,Position = 0)] [String] $LinkPath, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing '$LinkPath' from `$PSProfile.SymbolicLinks")) { Write-Verbose "Removing '$LinkPath' from `$PSProfile.SymbolicLinks" @($LinkPath,(Resolve-Path $LinkPath).Path) | Select-Object -Unique | ForEach-Object { if ($Global:PSProfile.SymbolicLinks.ContainsKey($_)) { $Global:PSProfile.SymbolicLinks.Remove($_) } } if ($Force -and (Test-Path $LinkPath)) { Write-Verbose "Removing SymbolicLink: $LinkPath" Remove-Item $LinkPath -Force } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileSymbolicLink -ParameterName LinkPath -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.SymbolicLinks.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileSymbolicLink' function Add-PSProfileVariable { <# .SYNOPSIS Adds a global or environment variable to your PSProfile configuration. Variables added to PSProfile will be set during profile load. .DESCRIPTION Adds a global or environment variable to your PSProfile configuration. Variables added to PSProfile will be set during profile load. .PARAMETER Name The name of the variable. .PARAMETER Value The value to set the variable to. .PARAMETER Scope The scope of the variable to set between Environment or Global. Defaults to Environment. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Add-PSProfileVariable -Name HomeBase -Value C:\HomeBase -Save Adds the environment variable named 'HomeBase' to be set to the path 'C:\HomeBase' during profile load and saves your PSProfile configuration. #> [CmdletBinding()] Param ( [Parameter(Mandatory,Position = 0)] [String] $Name, [Parameter(Mandatory,Position = 1)] [Object] $Value, [Parameter(Position = 2)] [ValidateSet('Environment','Global')] [String] $Scope = 'Environment', [Parameter()] [Switch] $Save ) Process { if (-not ($Global:PSProfile.Variables.ContainsKey($Scope))) { $Global:PSProfile.Variables[$Scope] = @{} } Write-Verbose "Adding $Scope variable '$Name' to PSProfile" $Global:PSProfile.Variables[$Scope][$Name] = $Value if ($Save) { Save-PSProfile } } } Export-ModuleMember -Function 'Add-PSProfileVariable' function Get-PSProfileVariable { <# .SYNOPSIS Gets a global or environment variable from your PSProfile configuration. .DESCRIPTION Gets a global or environment variable from your PSProfile configuration. .PARAMETER Scope The scope of the variable to get the variable from between Environment or Global. .PARAMETER Name The name of the variable to get. .EXAMPLE Get-PSProfileVariable -Name HomeBase Gets the environment variable named 'HomeBase' and its value from $PSProfile.Variables. .EXAMPLE Get-PSProfileVariable Gets the list of environment variables from $PSProfile.Variables. .EXAMPLE Get-PSProfileVariable -Scope Global Gets the list of Global variables from $PSProfile.Variables. #> [CmdletBinding()] Param ( [Parameter(Mandatory, Position = 0)] [ValidateSet('Environment','Global')] [String] $Scope, [Parameter(Position = 1)] [String] $Name ) Process { if ($Global:PSProfile.Variables.ContainsKey($Scope)) { if ($PSBoundParameters.ContainsKey('Name')) { Write-Verbose "Getting $Scope variable '$Name' from PSProfile" $Global:PSProfile.Variables[$Scope].GetEnumerator() | Where-Object {$_.Key -in $Name} } else { Write-Verbose "Getting $Scope variable list from PSProfile" $Global:PSProfile.Variables[$Scope] } } } } Register-ArgumentCompleter -CommandName Get-PSProfileVariable -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Variables[$fakeBoundParameter.Scope].Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Get-PSProfileVariable' function Remove-PSProfileVariable { <# .SYNOPSIS Removes a Variable from $PSProfile.Variables. .DESCRIPTION Removes a Variable from $PSProfile.Variables. .PARAMETER Name The name of the Variable to remove from $PSProfile.Variables. .PARAMETER Scope The scope of the Variable to remove between Environment or Global. .PARAMETER Save If $true, saves the updated PSProfile after updating. .EXAMPLE Remove-PSProfileVariable -Scope Environment -Name '~' -Save Removes the Environment variable '~' from $PSProfile.Variables then saves the updated configuration. #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param ( [Parameter(Mandatory, Position = 0)] [ValidateSet('Environment','Global')] [String] $Scope, [Parameter(Mandatory,Position = 1)] [String] $Name, [Parameter()] [Switch] $Save ) Process { if ($PSCmdlet.ShouldProcess("Removing $Scope variable '$Name' from `$PSProfile.Variables")) { Write-Verbose "Removing $Scope variable '$Name' from `$PSProfile.Variables" if ($Global:PSProfile.Variables[$Scope].ContainsKey($Name)) { $Global:PSProfile.Variables[$Scope].Remove($Name) } if ($Save) { Save-PSProfile } } } } Register-ArgumentCompleter -CommandName Remove-PSProfileVariable -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $Global:PSProfile.Variables[$fakeBoundParameter.Scope].Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } Export-ModuleMember -Function 'Remove-PSProfileVariable' New-Alias -Name 'Set-Prompt' -Value 'Switch-PSProfilePrompt' Export-ModuleMember -Alias 'Set-Prompt' New-Alias -Name 'Refresh-PSProfile' -Value 'Update-PSProfileConfig' Export-ModuleMember -Alias 'Refresh-PSProfile' New-Alias -Name 'Creds' -Value 'Get-MyCreds' Export-ModuleMember -Alias 'Creds' New-Alias -Name 'Switch-Prompt' -Value 'Switch-PSProfilePrompt' Export-ModuleMember -Alias 'Switch-Prompt' New-Alias -Name 'Edit-Prompt' -Value 'Edit-PSProfilePrompt' Export-ModuleMember -Alias 'Edit-Prompt' New-Alias -Name 'Save-Prompt' -Value 'Add-PSProfilePrompt' Export-ModuleMember -Alias 'Save-Prompt' New-Alias -Name 'Get-Prompt' -Value 'Get-PSProfilePrompt' Export-ModuleMember -Alias 'Get-Prompt' New-Alias -Name 'Remove-Prompt' -Value 'Remove-PSProfilePrompt' Export-ModuleMember -Alias 'Remove-Prompt' New-Alias -Name 'Load-PSProfile' -Value 'Import-PSProfile' Export-ModuleMember -Alias 'Load-PSProfile' # If we're in an interactive shell, load the profile. if ([Environment]::UserInteractive -or ($null -eq [Environment]::UserInteractive -and $null -eq ([Environment]::GetCommandLineArgs() | Where-Object {$_ -like '-NonI*'}))) { $global:PSProfile = [PSProfile]::new() $global:PSProfile.Load() Export-ModuleMember -Variable PSProfile } |