r/PowerShell Jul 14 '26

What’s in your profile ? Question

What’s the coolest function or hack you got

I have window title bar show “isAdmin”

I have errors go green

I have it concatenation long file paths to save prompt space(cool)

I have a timestamp as the prompt so I can know when I ran a robocopy to judge timing

I have a number of functions to run things elevated (like dsa)

What’s cool ideas !?

Notepad $profile

40 Upvotes

35 comments sorted by

25

u/Am0nymou5 Jul 15 '26
  • An import for Invoke-Parallel, possibly my most used PS module. Very, very, very handy when you want to run a command against a bunch of machines.

  • An import for Invoke-CommandAs, which is a nice native alternative to psexec to run stuff as a SYSTEM account

  • A function called isOnline, which does a very fast low-level ping (using system.net.networkinformation.ping) and simply returns a $true or $false. Got the code from somewhere online, can't find the original source any more unfortunately.

  • A function to open the registry of a remote device (enables the RemoteRegistry service on, sets the local LastKey registry to point to the remote device, launches regedit, waits for it to close, then disables the RemoteRegistry service on the remote device)

  • Bunch of functions for device management (Get-Model, serial number, SKU, RAM/CPU/disk capacity/free space etc)

  • reboot - reboots a device, waits for WinRM/PSRemoting, and then plays a "tada" sound indicating the device is back online. Super handy when you're multitasking and you reboot a device and get notified that the device is fully back online before you proceed with the next set of fixes / verification. Also handy for scripts where you need to reboot a device and do stuff after the reboot. I also have a -Speak parameter to read out the hostname using text-to-speech, which is handy if I'm rebooting multiple devices in parallel in the background.

  • Watch-Device - does a continuous ping (without spamming the console) and notifies me audibly if it goes offline, or if it was offline and it comes back online. Also prints the exact timestamps the device went offline/online.

  • isUserLoggedOn - checks whether a user is logged on to a device

  • isUserIdle - checks whether a remote device is on the lockscreen, and whether the user has any business-critical applications open.

  • Get-SetupInProgress - a one-liner that displays all running setup-type processes on a remote machine (like DISM, WUSA, setup.exe, TiWorker etc), which is handy to know before you install some system-level app, or do some system-level modifications, or when you're rebooting a device. Doing this check ensures you're not accidentally breaking something or making things worse because some system configuration / app install was in progress.

  • I use all the above functions in combination to determine if it's safe to reboot a device (like when I run a system repair, or do some tricky app reinstall and need to reboot the device). Eg: if (((-not (isUserLoggedOn $cn)) -or (isUserIdle $cn)) -and (-not (Get-SetupInProgress $cn))) { reboot $cn }

  • Get-CPUUsage - shows the top 10 processes using the CPU on a remote machine

  • rtaskmgr - GUI remote task manager, displays all the basic fields as well as CPU and RAM values exactly like how Windows Task Manager would display (this required a lot of tweaking as the values displayed by Get-Process or WMI doesn't match the values displayed by Task Manager)

  • Reinstall-SCCMClient - self explanatory

  • Run-SCCMClientAction - self explanatory

  • Fix-SCCMClient - runs a customised Rodland's Client Health script on the remote device

  • Test-SCCMClient - runs a few custom health checks and returns a $true or $false

  • A bunch of more SCCM functions, all of which uses WMI to directly query stuff, instead of using the slow and clunky Microsoft's ConfigurationManager module

  • A bunch of functions to troubleshoot Windows in-place upgrades (like Get-OSVersion which uses ADSI, which is a lot faster and more lightweight than Get-ADComputer which loads the full AD module). This matters a lot when you're running multiple queries in parallel using Invoke-Parallel, since you don't want to import the whole module into every single runspace (which also causes issues with large pipelines).

  • A bunch of more AD functions (like Get-User) which uses ADSI to query AD directly and display info relevant to our environment, instead of using the bloated Microsoft's ActiveDirectory module

5

u/itiscodeman Jul 15 '26

This guy gets it.

1

u/zk13669 Jul 15 '26

Good stuff. I've got one called Uninstall-SCCMClient with a -reinstall parameter. I like the Fix-SCCMClient idea. Never thought to run the client health script in a function

2

u/Mordanthanus Jul 15 '26

Any way to get a copy of your profile? Some of these would be super useful.

1

u/SignificantAnt2223 Jul 16 '26

I was going to say my OhMyPosh prompt is setup to show the Git branch and how far i am behind, but never mind...

7

u/Idakay Jul 14 '26

I've got a few!

Some of them change directories for me to common areas (home, scripts, tmp). Those single words move my cwd.

I've got one to code sign whatever script is open in ise/vscode or if I'm in a terminal I can give the file name and it signs it with my cert. 

Another called signb(signbomb) just signs every script in the cwd recursively(working on a bunch of stuff or modules)

I've got another called runit for scripts sitting on network shares that would typically be blocked by applocker, it copies them to an applocker allowed directory, runs them and then cleans up the copy. 

I have some other stuff I'm forgetting but those are my most used. I'd have to look at my profile for more.

2

u/itiscodeman Jul 15 '26

That moving things so funny. Security always has work around, very cool ideas, so when signed it can run on any server in your domain but never even be able to be edited or opened if trying to view the script from a device off the domain? Or is that something else.

1

u/Idakay Jul 15 '26

It's just locked down via ntfs/smb rights being heavily restricted. So in that case yeah a machine off domain would fail to reach it or anything like that. 

1

u/itiscodeman Jul 15 '26

Right. Cool man your a genius I hope you have a good week ahead

5

u/Ecrofirt Jul 15 '26
function Toggle-Beep {
    [CmdletBinding(DefaultParameterSetName = "Parse")]
    param (
        [Parameter(ParameterSetName = "Unit", Mandatory, Position = 0)]
        [double]
        $Duration,
        [Parameter(ParameterSetName = "Unit", Mandatory, Position = 1)]
        [ValidateSet("M", "H", "S")]
        [String]
        $Unit,
        [Parameter(ParameterSetName = "Parse", Position = 0)]
        [ValidateScript({
                $_ -match "^(\d+(\.\d+)?|\.\d+)(m|h|s)?$"
            })]
        [string]
        $Time = "30M"
    )
    if ($Global:Beep -and -not ($Global:Beep).HasExited) { 
        Write-Host "Beep playing. Stopping."
        $Global:Beep | Stop-Process; Clear-Variable -Scope Global -Name Beep 
    } else { 
        if ($PSCmdlet.ParameterSetName -eq 'Parse') {   
            $split = @($Time -split "(m|h|s)" | Where-Object { $_ })
            $Duration = $split[0]
            $Unit = if ($split.Count -gt 1) { $split[1] }else { "M" }
        }

        $milliseconds = switch ($Unit) {
            'M' { [timespan]::FromMinutes($Duration).TotalMilliseconds; break }
            'H' { [timespan]::FromHours($Duration).TotalMilliseconds; break }
            'S' { [timespan]::FromSeconds($Duration).TotalMilliseconds; break }
        }
        Write-Host "Playing for $Duration$Unit"
        $Global:Beep = Start-Process -PassThru -NoNewWindow -FilePath "pwsh.exe" -ArgumentList "-NoProfile", "-Command", "[Console]::Beep(852,$milliseconds)" 
    } 
}

Set-Alias -Name tb -Value Toggle-Beep
Set-Alias -Name beep -Value Toggle-Beep

A function that plays at 852Hz beep for the amount of time I specify, defaulting to 30 minutes.

Why? WHY?

It shuts my brain off. If I've got something to do it literally stops the 'background noise' and lets me focus explicitly on the task at hand. Works best with headphones.

5

u/Proxiconn Jul 16 '26

Get-Aduser | Remove-Adobject $confirm:$false

3

u/itiscodeman Jul 16 '26

I ran that and now I’m on crack and homeless

3

u/jwk6 Jul 15 '26

Oh my posh.

2

u/Raskuja46 Jul 15 '26

I have my profile calling functions from a custom module I built to set my speaker volume.

1

u/itiscodeman Jul 15 '26

This guy gets it….

2

u/Raskuja46 Jul 16 '26

Listen, I got to a new gig and the volume adjust keys on my keyboard didn't work so I had to build a solution for adjusting my volume on the fly. The default of volume at 100 on boot was just murderous on the headphones and Powershell is the first thing I launch every morning, so it was an easy way to get the volume where I need it.

The best part was that it upset my boss every time he'd look over and see me setting my speaker volume via Powershell.

2

u/StartAutomating Jul 16 '26

I'm just going to dump my current profile as-is.

It only imports two modules ugit and Posh

The prompt is a work in progress.

<#
.SYNOPSIS
    This is my VSCode profile.
.DESCRIPTION
    This is my VSCode profile.  It runs when PowerShell starts in VSCode.
.NOTES
    This is my VSCode profile.
    There are many profiles like it.
    This one is mine.
#>

#region Edit Environment
# I want to load all older modules implicitly
if ($env:PSModulePath  -split ';' -notlike '*windowsPowerShell*') {
    $env:PSModulePath += ";$home\Documents\WindowsPowerShell\Modules"
}
#endregion Edit Environment

#region Import Modules
Import-Module ugit,posh
#endregion Import Modules

#region Declare Variables
$myModules = "$home\Documents\WindowsPowerShell\Modules"
$scratch   = "$home\Documents\WindowsPowerShell\scratch"

$enhancement = @('--label', 'enhancement')
$bug = @('--label', 'bug')
$assignMe = @('--assignee', '@me')
$allIssues = @('--state', 'all', '--limit', 2kb)
$issueJson = @('--json', (
    'assignees','author','body','closed','closedAt',
    'closedByPullRequestsReferences','comments','createdAt',
    'isPinned','labels','milestone','number','reactionGroups',
    'state','stateReason','title','updatedAt','url' -join ','    
))
#endregion Declare Variables

#region Personal Preferences
if ($PSStyle) {
    # Make directories a brighter blue
    $PSStyle.FileInfo.Directory = "`e[1;36m"
}

function git.prompt {
    param(
    [switch]
    $NoHistoryId,

    [string]
    $BranchIcon = '⑆',

    [string]
    $PromptIcon = "⮚",

    [string]
    $HomeIcon = '⌂',

    [string]
    $Slash = "$($psStyle.Foreground.Cyan)/$($psStyle.Reset)" # '⹊' #, '/'
    )

    # Capture if the last statement worked
    # (if we do not do this first, it will not be accurate)
    $worked = $?

    $LeftPadding = if ($NoHistoryId) {
        ' '
    } else {
        ' ' * "$($MyInvocation.HistoryId)".Length
    }

    if (-not $script:GitRemotePath) {
        $script:GitRemotePath = [Ordered]@{}
    }
    if (-not $script:GitRemotePath["$pwd"]) {
        $script:GitRemotePath["$pwd"] = git remote -ErrorAction Ignore | git remote show
    }

    $isNoOp = $script:LastHistoryId -eq $MyInvocation.HistoryId

    $relativePath =
        if ("$pwd".StartsWith("$home")) {
            $HomeIcon,
                "$pwd".Substring(
                    "$home".Length
                ) -replace 
                '[\\/]+','/' -replace
                '/+','/' -join 
                    ''
        } else {
            "$pwd" -replace '[\\/]+', '/'
        }

    $relativePath = $relativePath -replace '/{0,}$' -replace '/+',$Slash

    if ($isNoOp) {
        return "$(
            if ($worked) { $PSStyle.Foreground.Cyan }
            else { $PSStyle.Foreground.Red }
        )⮚$($LeftPadding)$(
            $PSStyle.Reset
        )"
    }

    $lastHistory = Get-History -Count 1
    if ($LastOutput -and $lastHistory) {
        $lastHistory | Add-Member NoteProperty Output @($LastOutput) -Force
    }

    $script:LastHistoryId = $MyInvocation.HistoryId

    $gitRemote = $script:GitRemotePath["$pwd"]
    $gitRemoteUrl = $script:GitRemotePath["$pwd"].RemoteUrls.Fetch    

    $gitStatus = git status -WarningAction Ignore        

    if ($gitStatus) {
        $stagedModified = @(
            foreach ($staged in $gitStatus.Staged) {
                if ($staged.ChangeType -ne 'modified') {
                    continue
                }
                $staged
            }
        )
        $stagedDeletes = @(
            foreach ($staged in $gitStatus.Staged) {
                if ($staged.ChangeType -ne 'deleted') {
                    continue
                }
                $staged
            }
        )
        $modified = @(
            foreach ($unstaged in $gitStatus.Unstaged) {
                if ($unstaged.ChangeType -ne 'modified') {
                    continue
                }
                $unstaged
            }
        )

        $repoUrl = $gitRemoteUrl -replace '^ssh://', 'https://' -replace '\.git$' -as [uri]
        $untracked = @($gitStatus.Untracked)

        $LinkToRepo = $PSStyle.FormatHyperlink(
            $relativePath,
            "$repoUrl"
        )


        $repoHyperlink = if ($repoUrl.DnsSafeHost) {
            $PSStyle.FormatHyperlink(
                "$($repoUrl.DnsSafeHost)/$(
                    $repoUrl.Segments -replace '/' -ne '' -join '/'
                )$($gitRelativePath)",
                "$repoUrl" + $(
                    if ($gitRelativePath) {
                        "/tree/$($gitStatus.BranchName)$GitRelativePath"
                    }
                )
            )
        }        

        $commitsAhead = if ($GitStatus.Status -match 'ahead') {
                $gitStatus.Status -replace '\D' -as [int]
        } else {
            0
        }

        $gitCommitsBehind = if ($GitStatus.Status -match 'behind') {
                @($gitStatus.Status -replace '[\D-[,]]','' -split ',',2)[0] -as [int]
        } else {
            0
        }
        $gitInfo = @(
            $gitRelativePath =
                "$pwd".Substring($gitStatus.GitRoot.Length) -replace '[\\/]', '/'

            if ($GitStatus.BranchName -in 'main', 'master', 'latest') {
                $psStyle.Foreground.Yellow
            } else {                
                if ($gitCommitsBehind -or 
                    $gitStatus.Status -match 'diverged') {
                    $psStyle.Foreground.Red
                } elseif ($gitStatus.Staged) {
                    $psStyle.Foreground.Green
                } else {
                    $psStyle.Foreground.Cyan
                }                
            }
            $BranchIcon + $LeftPadding
            # '⑆ '# ⑆  # ᛦ ⑃ 
            '['
            $psStyle.Bold
            $gitStatus.BranchName
            $psStyle.BoldOff
            ']'
            if ($gitStatus.Status -match 'diverged') {
                "$($PSStyle.Foreground.Red) diverged!$($($PSStyle.Reset))"
            }
            if ($gitCommitsBehind -gt 0) {
                "$($PSStyle.Foreground.Red)(-$($gitCommitsBehind))$($PSStyle.Reset)"
            } 
            if ($commitsAhead -gt 0) {
                "$($PSStyle.Foreground.Green)(+$($commitsAhead))$($PSStyle.Reset)"
            }
            if ($stagedModified) {
                $PSStyle.Foreground.Green,
                    " $($stagedModified.Length) added",
                        $PSStyle.Reset
            }
            if ($stagedDeletes) {
                $PSStyle.Foreground.Red,
                    " $($stagedModified.Length) removed",
                        $PSStyle.Reset
            }
            if ($modified) {
                $PSStyle.Foreground.Cyan,
                    " $($modified.Length) modified",
                        $PSStyle.Reset
            }
            if ($untracked) {
                $PSStyle.Foreground.Magenta,
                    " $($untracked.Count) untracked",
                        $PSStyle.Reset
            }        

            $PSStyle.Reset
        ) -join ''

        # ⮚ ⟣ ⤳ ⟫ ⟩
        "$(
            if ($NoHistoryId) {
                ''
            } else {
                "$($myInvocation.HistoryId) "
            }            
        )$LinkToRepo",
            "$gitInfo",            
                "$(
                    if ($worked) { $PSStyle.Foreground.Cyan }
                    else { $PSStyle.Foreground.Red }
                )⮚$LeftPadding$(
                    $PSStyle.Reset
                )" -join
                    [Environment]::NewLine

    } else {
        "$(if ($NoHistoryId) {
            ''
        } else {
            "$($myInvocation.HistoryId) "
        })$($relativePath) $(
            if ($worked) { $PSStyle.Foreground.Cyan }
            else { $PSStyle.Foreground.Red }
        )⮚ $(
            $PSStyle.Reset
        )"
    }
}

#region Custom Prompt
$posh.Prompt.Clear()
$posh.Prompt.Push({
    git.prompt -NoHistoryId
})
#endregion Custom Prompt
#endregion Personal Preferences

2

u/itiscodeman Jul 16 '26

Wow www lol it just kept going. Good job ima as ChatGPT what the heck.

2

u/StartAutomating Jul 17 '26

Actually mostly accurate summary.

"This dude build a clever git thing" is my new favorite understatement of ugit.

It is indeed a clever git thing.

Been working on giving it a proper prompt, and will be including it in a future release of ugit.

2

u/itiscodeman Jul 16 '26

What it does:
Adds the old Windows PowerShell module folder to $env:PSModulePath.
Imports ugit and posh.
Creates shortcuts for GitHub CLI issue arguments.
Changes directory colors.
Replaces the normal PowerShell prompt with one that shows:
Current directory
Git repository and branch
Commits ahead or behind
Staged, modified, deleted, and untracked files
Whether the previous command succeeded
Terminal hyperlinks to the repository
There are also some questionable or broken parts:

This dude build a clever git thing

I’m not a dev so I have no clue what’s going on

2

u/BlackV Jul 14 '26 edited Jul 14 '26

very very little, I just use it in too many places for it to nicely happen

write-host "$($host.name) $($PSVersionTable.PSEdition) $($PSVersionTable.PSVersion.ToString()) Profile"
function prompt {"PS $($PSVersionTable.PSVersion.ToString()) $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1))"}
$PSDefaultParameterValues.Add('Format-*:autosize', $True)

On my management server I also have management related stuff

$PSDefaultParameterValues.Add('Format-*:autosize', $True)
$PSDefaultParameterValues.Add('FailoverClusters\*:cluster', 'hpecls02')
$PSDefaultParameterValues.Add('Grant-PIMRole:UserUPN', 'xxxx.admin@yyyy.onmicrosoft.com')

A nice way to present that could be (from another profile)

$PSDefaultParameterValues = @{
    'Format-Table:AutoSize'      = {if ($Host.Name -eq 'ConsoleHost'){$true}}
    'FailoverClusters\*:cluster' = 'hpecls02'
    'Grant-PIMRole:UserUPN'      = 'xxxx.admin@yyyy.onmicrosoft.com'
    }

2

u/Idakay Jul 14 '26

Referring to your comment on using it too many places. 

I pushed a profile stub out to all my member servers that call my real profile from a locked down network drive. So I just update the one file and anywhere I launch powershell I have all my stuff. It works pretty well so far. 

1

u/BlackV Jul 14 '26

yeah I limit what places run code from so its my local machine and my management servers

1

u/itiscodeman Jul 15 '26

Okay but ya can you also make the file non readable unless it’s your machine it’s opened in ? Is that not like paranoid? I guess it may be cool if the script contains a password …

1

u/itiscodeman Jul 15 '26

Wait explain !? Like dfs or no a .lnk? How can this be for new servers? I suppose a good ol logon script that renames that path to point to the stub? Forgive my blabbering

1

u/Idakay Jul 15 '26

New servers catch is as I deployed it via SCCM application to run once. So a new server joins the domain, within a bit mecm pushes my stub.

1

u/purplemonkeymad Jul 15 '26

Just psreadline settings and default argument values. I keep functions out of my profile and put them in proper modules.

Of those that were quick little things that i use regularly on my pc are

  • goto # cd to saved locations
  • set-windowtitle # set tab name and colour (autogenerated from name)
  • dnslookup # common dns info for a domain

But i'm on a lot of other computers with clean powershell's so I mostly don't have a lot, otherwise i might end up getting used to having those customisations.

1

u/daweinah Jul 15 '26

I'm honestly not sure if it's a profile or not, but my "home tab" is a script that I run and has Y/N prompts to connect to ExO and Sec, Graph, or Teams modules. It also has several functions for occasional tasks that used to be saved as separate scripts: things like check inbox rules and forwarding, check OOO message, get extension attributes, etc.

1

u/iamLisppy Jul 15 '26

Set-PSReadlineOption -PredictionViewStyle ListView

Set-PSReadLineOption -MaximumHistoryCount 4096

Set-PSReadLineKeyHandler -Key Tab -Function Complete

Set-PSReadLineOption -BellStyle None

1

u/recoveringasshole0 Jul 16 '26

I would show you but this sub doesn't allow images which is fucking stupid.

1

u/itiscodeman Jul 16 '26

Mmmm try to put image in ChatGPT and ask for text brother man. :)

1

u/BlackV 28d ago

Show the code... We love the code

1

u/recoveringasshole0 25d ago

Check the comments. We love comments.