Tag Archives: $PROFILE

Keeping a Continuous Total


Notice: The following post was originally published on another website. As the post is no longer accessible, it is being republished here on tommymaynard.com. The post was originally published on July 15, 2019.


There’s a function stored in my $PROFILE (CurrentUserAllHosts). Maybe you’ve read about it before; it’s come up. Its purpose is to go out to the PowerShell Gallery and determine how many downloads I have of each PowerShell script and/or module I’ve published there. In the past, my tool provided a download total for each project along with a single total of all the downloads. Today that changes, but before it does (I seriously haven’t the updated code yet — typical), let’s show the code and results, as it currently stands.

Function Showw-PSGalleryProject {
    Param (
        [System.Array]$Projects = ('TMOutput','Start-1to100Game3.0','Get-TMVerbSynonym','SinkProfile','Show-PSDriveMenu','Switch-Prompt')
    )

    Foreach ($Project in $Projects) {
        If (Find-Module -Name $Project -ErrorAction SilentlyContinue) {
            $TempVar = Find-Module -Name $Project
        } ElseIf (Find-Script -Name $Project) {
            $TempVar = Find-Script -Name $Project
        }
        [PSCustomObject]@{
            Name = $TempVar.Name
            Version = $TempVar.Version
            Downloads = $TempVar.AdditionalMetadata.downloadCount
        }
        [int]$TotalDownloads += $TempVar.AdditionalMetadata.downloadCount
    }
    ">> Total downloads: $TotalDownloads"
} # End Function: Show-PSGalleryProject.
Show-PSGalleryProject
Name                Version Downloads
----                ------- ---------
TMOutput            1.1     1193
Start-1to100Game3.0 3.0     170
Get-TMVerbSynonym   1.4     163
SinkProfile         1.0     100
Show-PSDriveMenu    1.1     69
Switch-Prompt       1.2.0   84
>> Total downloads: 1779

See how we only have the single Total downloads above (the 1779 value)? I’ve decided I don’t like that, so it’s about to change. I want the total downloads as each of these objects are being returned (, calculated,) and written.

In the newest version of this little $PROFILE-based function — there was an earlier one — the first thing I’ve done is added the [CmdletBinding] Attribute. It’s to add the OutVariable Common Parameter. We’ll see how that’s helpful momentarily. The second thing we’ve done is begun collecting and displaying, what I’ve called the Type (find in the below code: ; $Type = Module or Script). This will differentiate when added to each object, whether one of my projects in the PowerShell Gallery is a module or script. Here’s the updated code with these two additions. Notice also, the object created inside [PSCustomObject] now includes a Type property. This is where the Module or Script determinations are included.

Function Show-PSGalleryProject {
    [CmdletBinding()]
    Param (
        [System.Array]$Projects = ('TMOutput','Start-1to100Game3.0','Get-TMVerbSynonym','SinkProfile','Show-PSDriveMenu','Switch-Prompt')
    )

    Foreach ($Project in $Projects) {
        If (Find-Module -Name $Project -ErrorAction SilentlyContinue) {
            $TempVar = Find-Module -Name $Project; $Type = 'Module'
        } ElseIf (Find-Script -Name $Project) {
            $TempVar = Find-Script -Name $Project; $Type = 'Script'
        }
        $TotalDownloads = [int]$TotalDownloads + [int]$TempVar.AdditionalMetadata.downloadCount
        [PSCustomObject]@{
            Name = $TempVar.Name
            Type = $Type
            Version = $TempVar.Version
            Downloads = $TempVar.AdditionalMetadata.downloadCount
            TotalDownloads = $TotalDownloads
        }
    } # End Foreach.
} # End Function: Show-PSGalleryProject.
Show-PSGalleryProject

The other recent addition to this code is the addition of my $TotalDownloads variable. Through each iteration, we update it so it has the current value and then displays said current value with the current object. I may have been a little too generous with my [int] casting; however, do remember that the plus sign (+) is also a concatenation operator. I want a total download count — not a string of individual download counts, strung together.

In closing, there’s something else to remember. By default (and there are ways around this), when we have more than four properties being returned per object, the “table” output becomes a “list” output — have a look.

Name           : TMOutput
Type           : Module  
Version        : 1.1     
Downloads      : 1193    
TotalDownloads : 1193    

Name           : Start-1to100Game3.0
Type           : Script
Version        : 3.0   
Downloads      : 170   
TotalDownloads : 1363  

Name           : Get-TMVerbSynonym
Type           : Script
Version        : 1.4   
Downloads      : 163   
TotalDownloads : 1526  

Name           : SinkProfile
Type           : Module
Version        : 1.0   
Downloads      : 100   
TotalDownloads : 1626  

Name           : Show-PSDriveMenu
Type           : Script
Version        : 1.1   
Downloads      : 69    
TotalDownloads : 1695  

Name           : Switch-Prompt
Type           : Script
Version        : 1.2.0 
Downloads      : 84    
TotalDownloads : 1779

While using the newly added OutVariable is helpful if I want to display the above output and capture the output into a variable at the same time, here’s how I run this now.

$PS = Show-PSGalleryProject
$PS | Format-Table -AutoSize
Name                Type   Version Downloads TotalDownloads
----                ----   ------- --------- --------------
TMOutput            Module 1.1     1193                1193
Start-1to100Game3.0 Script 3.0     170                 1363
Get-TMVerbSynonym   Script 1.4     163                 1526
SinkProfile         Module 1.0     100                 1626
Show-PSDriveMenu    Script 1.1     69                  1695
Switch-Prompt       Script 1.2.0   84                  1779

Next up is likely putting this code into a background job; it’s not the quickest thing I’ve written (although I blame that on the speed of the PowerShell Gallery lookup process, perhaps). Maybe a background job that runs in, or starts at, the end of the profile script. This, in order that these slow-to-obtain results are available sooner and with minimal impact.

Edit: 2/5/2022 –  I never did do that, although this function does still exists in my $PROFILE script. Now that I have seen this post again, perhaps there will be another push to try that. Time will tell. Oh, and here are my updated totals since this post was first written and published.

Name                Type   Version Downloads TotalDownloads
----                ----   ------- --------- --------------
TMOutput            Module 1.1     2980                2980
Start-1to100Game3.0 Script 3.0     266                 3246
Get-TMVerbSynonym   Script 1.4     293                 3539
SinkProfile         Module 1.0     302                 3841
Show-PSDriveMenu    Script 1.1     186                 4027
Switch-Prompt       Script 1.2.0   236                 4263

Variables, and More About Note Properties

I have been thinking a lot about variables and note properties recently. You know that, if you read A Better Way to Solve the Same Problem. It sets the stage for what we are going to see today. The question that started that post was, whether or not I could add note properties to a single variable, such as PowerShell does with the $PROFILE variable. I could. I ended up creating a variable that held one URL when you returned the variable but also held four other URLs, one in each additional note property.

I want to take this further, and the best part is that all the things I was worried about trying to do myself, PowerShell just does for me without any thinking about it. Follow along and you will see.

Let’s set up the variable I was using last time to help this recap. Then we will create another variable and work with it to explore some new things I learned. Building this variable is being done a little differently than it was previously. It incorporates some concepts from another recent post, There is a Difference: Arrays Versus Hash Tables. First, we will assign our initial value to the variable.

$PSSites = 'https://docs.microsoft.com/en-us/powershell/scripting/overview'

Next, we will create a hash table that includes four key-value pairs. Each pair includes a website name and an associated URL, because as we learned in that post, a hash table is an associative array.

$Sites = @{
    GitHub  = 'https://github.com/PowerShell/PowerShell'
    Docs    = 'https://docs.microsoft.com/en-us/powershell/'
    Gallery = 'https://www.powershellgallery.com/'
    Reddit  = 'https://www.reddit.com/r/PowerShell/'
}

Following that, we will use the foreach statement to loop through the key-value pairs in the hash table, running Add-Member against each of them, and thusly adding each as a note property on our $PSSites variable.

foreach ($Site in $Sites.GetEnumerator()) {
    $PSSites = $PSSites | Add-Member -NotePropertyName $Site.Key -NotePropertyValue $Site.Value -PassThru
}

We can use Get-Member to ensure the previous command added the additional URLs as note properties.

$PSSites | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name    MemberType   Definition
----    ----------   ----------
Docs    NoteProperty string Docs=https://docs.microsoft.com/en-us/powershell/
Gallery NoteProperty string Gallery=https://www.powershellgallery.com/
GitHub  NoteProperty string GitHub=https://github.com/PowerShell/PowerShell
Reddit  NoteProperty string Reddit=https://www.reddit.com/r/PowerShell/

At this point, we have our own variable like the $PROFILE variable that contains multiple string values. We can access the note properties of $PSSites using dot notation.

$PSSites.GitHub
$PSSites.docs
$PSSites.Gallery
$PSSites.Reddit

https://github.com/PowerShell/PowerShell
https://docs.microsoft.com/en-us/powershell/
https://www.powershellgallery.com/
https://www.reddit.com/r/PowerShell/

Now, let’s create another variable and add some members to it, as well. These will be a little different and you will see that momentarily. We will start by assigning a string value to the variable, as we did above.

$TestingVariable = 'A string value'
$TestingVariable
A string value

Before we add the rest, I suppose I should remind everyone about my end goal written at the bottom of the first post linked above: Save the previous value of a variable to a note property when assigning a new value to the variable itself.

This would be much easier if we could guarantee that every new value assigned to the variable was a string, and therefore, every old value property would also be a string. My worries, as mentioned earlier, were about arrays, hash tables, ordered dictionaries, and other data structures I may have to save to a note property. Can other data structures like those be put in a note property? Let’s work through these data structures and see if we can get them into note properties, to begin with. In this example, we will add two arrays to two separate note properties, as Array01 and Array02. One of these will and one will not use the array, sub-expression operator (@()).

$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'Array01' -NotePropertyValue 1,2,3 -PassThru
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'Array02' -NotePropertyValue @(4,5,6) -PassThru

Here, we will create a hash table and store it in the $HashTable variable. We will then use that variable as a part of another Add-Member command.

$HashTable = @{Dad = 'David'; Mom = 'Betty'; Daughter = 'Janice'; Son = 'Bryan'}
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'HashTable' -NotePropertyValue $HashTable -PassThru

Finally, we’ll create an ordered dictionary and add it as a note property, as well. If the hash table takes, then it is probably safe to assume this will take, too.

$OrderedDictionary = [ordered]@{
    Monday    = 'Chipotle'
    Tuesday   = 'Pizza'
    Wednesday = 'Mahi Mahi';
    Thursday  = 'Ravioli'
    Friday    = 'Orange Chicken'
}
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'OrderedDictionary' -NotePropertyValue $OrderedDictionary -PassThru

At this point, we should probably see what Get-Member returns about the note properties we have assigned to our $TestingVariable. We have yet to test that anything has actually been added so far, although we have not received any errors stating otherwise. In the below example, we can see the four, note properties that we have added. The best part, the definition property indicates it knew the data structure of these when they were added as note properties. The arrays show object[], the hash table, hashtable, and the ordered dictionary, OrderedDictionary.

$TestingVariable | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name               MemberType   Definition
----               ----------   ----------
Array01            NoteProperty Object[] Array01=System.Object[]
Array02            NoteProperty Object[] Array02=System.Object[]
HashTable          NoteProperty hashtable Hash Table=System.Collections.Hashtable
OrderedDictionary  NoteProperty OrderedDictionary Ordered Dictionary=System.Collections.Specialized.OrderedDictionary

Let’s take a closer look at each of our new note properties. For each of them, we will run GetType() against the note property and then return the value it has stored. When we return a note property that contains an array, it returns that. Same for the others; the data structure was recognized and retained when it was added as a member. And it is respected on the way out, too. I cannot tell you how grateful I am that I do not have to be the one to figure out the underlying data structure of a value going in and then coming back out. This is great news for some of the other things I have planned.

$TestingVariable.Array01.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

$TestingVariable.Array01
1
2
3

$TestingVariable.Array02.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

$TestingVariable.Array02
4
5
6

$TestingVariable.HashTable.GetType()       

IsPublic IsSerial Name                                     BaseType     
-------- -------- ----                                     --------     
True     True     Hashtable                                System.Object

$TestingVariable.HashTable

Name                           Value
----                           -----
Daughter                       Janice
Son                            Bryan
Dad                            David 
Mom                            Betty

$TestingVariable.OrderedDictionary.GetType()

IsPublic IsSerial Name                                     BaseType      
-------- -------- ----                                     --------      
True     True     OrderedDictionary                        System.Object 

$TestingVariable.OrderedDictionary

Name                           Value
----                           -----
Monday                         Chipotle
Tuesday                        Pizza
Wednesday                      Mahi Mahi
Thursday                       Ravioli
Friday                         Orange Chicken

A Better Way to Solve the Same Problem

There once was a time I wrote a post. Realistically, there have been many times. Like many posts though, I was proud of it. I can’t show it to you, as it wasn’t written on my site and it has since been lost. I am actually kind of embarrassed by it these days, so I’m mostly glad it is gone.

Someone, on some forum, wanted to know the previous value of a variable. Not the current value, but the last value it had been assigned before its current value. My idea then, which seems ridiculous now, was to store its last value in its description property. If you don’t know, variables have a writable description property; really, they do. It is a pretty nice feature, were anyone to actually use it. I don’t imagine many people do. I know about it, and honestly, I don’t use it.

Instead of using a variable’s description property to store its last value, I got to thinking about the $PROFILE variable and how it works. Let’s have a look, but before we do, let’s make sure we all know what this variable holds. This variable contains a path to your profile script, whether your profile script exists or not.

[PS7.2.0][C:\] $PROFILE
C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

The profile script is invoked when PowerShell launches, allowing you to create variables, define functions, and modify your PowerShell environment to suit you. In a recent post, I discussed creating a work-based ASCII image and sending in some keys. This made sure the first command I want to invoke was ready when PowerShell launched; all I had to do was press Enter. Oh, and here is every time I mentioned the $PROFILE variable on tommymaynard.com going all the way back to 2014. It has come up quite a few times over the years.

We have seen the value the $PROFILE variable holds, but there’s more. Let’s run it another way.

[PS7.2.0][C:\] $PROFILE | Select-Object -Property *

AllUsersAllHosts : C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\tommymaynard\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Length : 71


Using the Select-Object cmdlet produced three additional paths: AllUsersAllHosts, AllUsersCurrentHost, and CurrentUserAllHosts. I am not going to go into what these all mean, but host refers to the program hosting the PowerShell engine. Read more about profiles here. With these three additional paths, there are four total locations where a profile script can be located and loaded by default. So, what do we have here? We have a variable with the ability to hold more than a single value (without making it an array or a hash table). That’s just how this variable works and I want the same thing for my example variable.

In this example, we’ve returned the same values as we did above, however, in this instance, we’re using the Get-Member cmdlet. I think it is important to see the values returned a couple of different ways.

[PS7.2.0][C:\] $PROFILE | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name                   MemberType   Definition
----                   ----------   ----------
AllUsersAllHosts       NoteProperty string AllUsersAllHosts=C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost    NoteProperty string AllUsersCurrentHost=C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts    NoteProperty string CurrentUserAllHosts=C:\Users\tommymaynard\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost NoteProperty string CurrentUserCurrentHost=C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

Let’s start the remainder of this post by creating my example variable. Like the $PROFILE variable, we’re just going to work with string values.

[PS7.2.0][C:\] $PROFILE.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites = 'https://docs.microsoft.com/en-us/powershell/scripting/overview'
[PS7.2.0][C:\] $PSSites
https://docs.microsoft.com/en-us/powershell/scripting/overview
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

[PS7.2.0][C:\]

At this point, we’ve assigned a docs.microsoft.com URL directly to the $PSSites variable. Now, let’s take a few new values and assign each of them as a NoteProperty on the same variable. This is done by using the Add-Member cmdlet. We assign the current value of the $PSSites variable back to the $PSSites variable, while also adding the new note property.

[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'GitHub' -NotePropertyValue 'https://github.com/PowerShell/PowerShell' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'Docs' -NotePropertyValue 'https://docs.microsoft.com/en-us/powershell/' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'Gallery' -NotePropertyValue 'https://www.powershellgallery.com/' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyMembers @{Reddit='https://www.reddit.com/r/PowerShell/'} -PassThru

You may have noticed, but in the last of the above four examples, we used the NotePropertyMembers parameter with a hash table for the associated value. This option allows us to supply both the name and value at the same time. It does exactly what the first three examples did — it is just another way to do it.

I should mention the PassThru switch parameter. This is a requirement because Add-Member cannot add types to strings, so this will generate an object. It’s worth keeping this in mind. Without it, you’ll drive yourself crazy trying to determine why it isn’t working — always, read the help!

Using dot notation we can see the additional values (note properties), that our variable has been assigned.

[PS7.2.0][C:\] $PSSites.GitHub
https://github.com/PowerShell/PowerShell
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Docs
https://docs.microsoft.com/en-us/powershell/
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Gallery
https://www.powershellgallery.com/
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Reddit
https://www.reddit.com/r/PowerShell/

Like we did with the $PROFILE variable, we can use the Select-Object cmdlet to return all of our URLs. We can also use Get-Member.

[PS7.2.0][C:\] $PSSites | Select-Object -Property *

GitHub   : https://github.com/PowerShell/PowerShell
Docs     : https://docs.microsoft.com/en-us/powershell/
Gallery  : https://www.powershellgallery.com/
Reddit   : https://www.reddit.com/r/PowerShell/
Length   : 62


[PS7.2.0][C:\] $PSSites | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name     MemberType   Definition
----     ----------   ----------
Docs     NoteProperty string Docs=https://docs.microsoft.com/en-us/powershell/
Gallery  NoteProperty string Gallery=https://www.powershellgallery.com/
GitHub   NoteProperty string GitHub=https://github.com/PowerShell/PowerShell
Reddit   NoteProperty string Reddit=https://www.reddit.com/r/PowerShell/

Now, with the PowerShell site URLs stored in the $PSSites variable, I can easily open whichever website I prefer using Start-Process and the corresponding property. No real idea if I’ll use this, but I’m grateful to know how to use a note property to store additional data along with a simple string variable.

[PS7.2.0][C:\] Start-Process -Path $PSSites.Gallery

Join me next time, perhaps, if I can devise a way to save the previous value of a variable to a note property when assigning a new value to the variable itself. It will take a little thought and work, but for now, I think it is possible.

Prep Command for Immediate Use

Edit: I’ve updated this post by adding a really quick video at the bottom — check it out. It briefly shows the results of the concepts in this post.

It was just yesterday when I wrote the Windows Terminal Fun Surprise post. This was a short post about executing the Alt+Shift+D keyboard key combination inside Windows Terminal. I got myself to a point where I could programmatically send in this key combination using the below commands.

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait('%+D')

This got me thinking: Can I use this to prep a command for immediate use? I know, what does that even mean? My profile script $PROFILE, contains a good deal of helpful tools. And then it has something like the PowerShell ASCII art below. It’s actually a work logo, but you get the idea. This laptop with the PowerShell logo will stand in just fine, however.

Function Show-PowerShellAsciiArt {
@"
   ._________________.
   |.---------------.|
   ||   PowerShell  ||
   ||     \         ||
   ||      \        ||
   ||      /        ||
   ||     / ------  ||
   ||_______________||
   /.-.-.-.-.-.-.-.-.\
  /.-.-.-.-.-.-.-.-.-.\
 /.-.-.-.-.-.-.-.-.-.-.\
/______/__________\___o_\
\_______________________/

"@
} # End Function: Show-PowerShellAsciiArt.
Show-PowerShellAsciiArt

As cool as I think this logo is — the one I actually use — and as certain as I am about it appearing every time I open a new terminal, I feel like it’s in the way when I want to start to use my terminal. Like you, I suspect, I want my prompt and cursor in the top-leftish corner and ready to go pretty quickly. The difference is, I’m willing a momentary slowdown. I can do better, however.

The thought was: Can I place a command at the prompt as a part of my profile script, that will allow me to just press Enter to clear the screen? You see, I usually type cls or clear, press Enter and then start with whatever I’m doing. I want the logo, but I also want to get started as soon as I can. The answer to that most recent thought though is yes.

In addition to the above Show-PowerShellAsciiArt function, is one more brief function and a modification to the code that started off this post.

Function Clear-Logo {Clear-Host}

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait('Clear-Logo')

Now, when my profile script executes, it places the “Clear-Logo” text at my prompt, allowing me to just press Enter to clear the image and start doing what I do at the command line. As you can see, Clear-Logo is only there to invoke Clear-Host. Still, I like it better.

Press Enter and voilà.

In order to get a better idea of how this works — just in case, I didn’t do a great job explaining it, who knows — I’ve included a short video below. This shows me opening two new tabs in Windows Terminal and pressing Enter after each is open, and therefore, clearing the screen (the logo).

The Other $PROFILEs

If you weren’t aware, I did a great deal of writing and posting recently due to my idea to complete chapter reviews of The Pester Book. I think it was a success, and if anything, it helped force me to ensure I fully understood the content for the things I covered here. I’m not confident with things until I fully understand them, and so even though I finished that book, I’m still after additional content.

Well, after all the writing, I did a couple other posts, but the last one I left off on was about an old HTA I wrote. An HTA is an HTML application. It’s old, but it allowed (back when I used and wrote it), people like me to create GUIs for VBS scripts. Yes, VBS. I hate that it’s the first post on the front page, so I had to write something new in relation to PowerShell.

Today, I wanted to take a moment and remind everyone about the $PROFILE variable. If you enter this variable into your PowerShell host program you’ll see a path to the current user’s, current host’s, PowerShell profile script. A couple things: One, the host here is the PowerShell host, as in the ConsoleHost, the ISE, Visual Studio Code, etc. It has nothing to do with a computer host.

Two, a profile script –once it’s been created and edited– runs when you open the host program, and allows you to set up the environment, or PowerShell session, the way you want it. You can create aliases, variables, and define functions, and ensure they’re available when you open that PowerShell host program.

Here’s how you can check the currently used host program.

PS > $Host.Name
ConsoleHost

When I enter $PROFILE, it returns to me the below path. Do notice that I’m using Windows PowerShell in this example. With PowerShell 6.0 running, it would’ve indicated “PowerShell” in the path, and not “WindowsPowerShell.”

PS > $PROFILE
C:\Users\tommymaynard\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Now, it should be noted that there’s actually more profile scripts that can execute when you open a PowerShell host program. Take a look here.

PS > $PROFILE | Select-Object -Property *

AllUsersAllHosts       : C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost    : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts    : C:\Users\tommymaynard\Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\tommymaynard\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
Length                 : 78

The above Select-Object command returns five properties, and four of them are paths. In addition to the CurrentUserCurrentHost property, that we saw when we didn’t use Select-Object, we also have entries for CurrentUserAllHosts, and two for all users: AllUsersAllHosts and AllUsersCurrentHost. It’s too bad that in Windows PowerShell the desire to use an AllUsers profile script required work in the C:\Windows\System32 directory. That’s changed in PowerShell Core, where AllUsers profile scripts default to C:\Program Files\PowerShell\<version>. Just because we have a path in these properties, does not mean there’s a file with these indicated names. The files have to be created with New-Item in order to make use of these profile scripts.

Keep this in mind as you use profile scripts. While I suspect most people don’t use it, CurrentUserAllHosts, is better, than using CurrentUserCurrentHost if you want the same thing in every profile. There was a time I used CurrentUserCurrentHost in the Console and then had all my other hosts dot source that script in their own profile scripts. Dumb. If you need something in all, use CurrentUserAllHosts, but if you need something in one host vs. another, then use CurrentUserCurrentHost.

Now, back to that project I’m working. It has everything to do with profile scripts, and with this post I can be certain that I’ve provided myself some reminders about the $PROFILE variable. Additionally, I’ve gotten that HTA post moved down one; it’s no longer the first post on my site.

Technical Fact at PowerShell Launch

When I read books, or websites, and find worthy facts, I aim to try and keep them. If I read a book, my bookmark is often a few pieces of paper stapled together with info and page numbers from the book. Well, I’m scrapping that technique, which went hand-in-hand with folding down page corners. I’m also ditching the small pieces of paper that litter my desk with random bits of information: “PowerShell objects come from classes,” and “LastLogonDate is the converted version of LastLogonTimeStamp.” Now, it’s all slated to go in a single file called InfoLines.txt in Dropbox, here: C:\Users\tommymaynard\Dropbox\PowerShell\Profile.

If you’re wondering why I use a Dropbox folder it’s because I want this file and its worthy facts to be available regardless of whether I’m on my work, or home computer. You can read more in a post I wrote that uses Dropbox to sync my profile script between work and home, here: http://tommymaynard.com/sync-profile-script-from-work-to-home-2017. It may help make what I’m doing here make more sense.

For now, because I just started this today, I only have a few lines of worthy information. Here’s the contents of my InfoLines.txt file so far. If you can’t tell, I’m finishing up Amazon Web Services in Action. I only have 70 more pages to go!

The auto-scaling group is responsible for connecting a newly launched EC2 instance with the load balancer (ELB). (AWS in Action, p.315)
DevOps is an approach driven by software development to bring development and operations closer together. (AWS in Action, p.93)
Auto-scaling is a part of the EC2 service and helps you to ensure that a specified number of virtual servers are running. (AWS in Action, p.294)

Each time I open the ConsoleHost, the ISE, or Visual Studio Code, I want a random line from the file to be shared with me. The below code will return a single, random line with asterisks both above and below it. This is in order to help separate it from the (totally unnecessary, unwelcome, and shouldn’t even be there) message that tells me how long my “personal and system profiles” took to load, and my prompt. They need to put that message in a variable and not on my screen without permission. Don’t tell us what you think we need to know, guys.

'**************'
Get-Content -Path "$env:USERPROFILE\Dropbox\PowerShell\Profile\InfoLines.txt" | Get-Random
'**************'

That’s it. Now, whenever I open one of these PowerShell hosts, I’ll get a quick reminder about something I found important, and want to keep fresh in my mind.

Update: I decided I wanted the asterisks above and below my technical fact to go from one end of the PowerShell host to other. Here’s how I did that.

'*' * ($Host.UI.RawUI.BufferSize.Width - 1)
Get-Content -Path "$env:USERPROFILE\Dropbox\PowerShell\Profile\InfoLines.txt" | Get-Random
'*' * ($Host.UI.RawUI.BufferSize.Width - 1)

Sync Profile Script from Work to Home

After a few years of this whole I-do-PowerShell-everyday thing, my profile script has become quite the necessity at both work, and home. In fact, there’s much in my work profile script that I want at home, such as my prompt function, among others. With this ever present need, I set off to create a profile script that self-updates between my work and home computer, without any continuing need for me to do it myself.

So, about the profile script, just in case you ended up here and are clueless to that in which I’m referring. There’s a potential .ps1 file that can be created and edited so that every time you open the PowerShell ConsoleHost things run in the background, as the session begins. I use it to initialize variables, to create aliases, to declare functions, and more. In regard to Profiles, here’s an old Scripting Guy article that may be of help if you need it. I may have started there myself a few years ago.

I’ve broken the structure of my profile script template into three logical parts. Have a look at the first section of the template below, and then we’ll discuss it.

$WorkComputer = 'WorkComputer'
$HomeComputer = 'HomeComputer'

Switch ($env:COMPUTERNAME) {

    # Work computer only.
    {$_ -eq $WorkComputer} {

    } # End work computer only.

    # Home computer only.
    {$_ -eq $HomeComputer} {

    } # End home computer only.

    # Work and home computer.
    {$_ -eq $WorkComputer -or $_ -eq $HomeComputer} {
 
    } # End Work and home computer.
} # End Switch.

In lines 1 and 2, we create two variables, $WorkComputer and $HomeComputer. As you might expect, these variables store the computer names of my work, and my home computers. If you choose to use this template, then these variable assignments will require a small bit of manual editing on your own; however, it’s a one time thing, and in my mind, a small price to pay to have your profile synced between two computers.

Following these two variable assignments, is the above Switch statement. This defines what will be available on the two different computers. The first section in the Switch are things I only want available on my work computer, the second section are things I only want available on my home computer only, and the last section is for things that I want available on both my work and home computer. This last section is where I placed my prompt function, for instance, in order that it’s available regardless of where I’m working. I really don’t want to be without it.

At the end of this post, I’ll included the full, uninterrupted code, but for now, let’s have a look at the next section. This section defines the Sync-ProfileScript function.

# Create Sync profile function.
Function Sync-ProfileScript {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateSet('Source','Destination')]
        [string]$ComputerType,

        [Parameter()]
        [string]$LocalProfileScriptPath = "$env:USERPROFILE\Dropbox\PowerShell\Profile\Microsoft.PowerShell_profile.ps1"
    )

    Begin {
        # Exit if NOT the ConsoleHost.
        If (-Not($Host.Name -eq 'ConsoleHost')) {
            break
        }
    } # End Begin.

    Process {
        If (Test-Path -Path $PROFILE) {
            $CompareFiles = Compare-Object -ReferenceObject (Get-Item $PROFILE).LastWriteTime -DifferenceObject (Get-Item $LocalProfileScriptPath).LastWriteTime -IncludeEqual |
                Select-Object -First 1

            If ([System.Boolean]($CompareFiles.SideIndicator -ne '==')) {

                Switch ($ComputerType) {
                    'Source' {Copy-Item -Path $PROFILE -Destination $LocalProfileScriptPath}

                    'Destination' {
                        Copy-Item -Path $LocalProfileScriptPath -Destination $PROFILE

                        'Profile Script has been updated. Restart ConsoleHost?'
                        Do {
                            $Prompt = Read-Host -Prompt 'Enter r to Restart or c to Cancel'
                        } Until ($Prompt -eq 'r' -or $Prompt -eq 'c')

                        If ($Prompt -eq 'r') {
                            Start-Process -FilePath powershell.exe
                            Stop-Process -Id $PID
                        }
                    }
                } # End Switch.
            } # End If.
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Sync-ProfileScript.

The purpose of the Sync-ProfileScript function is to copy the profile script to a Dropbox folder, when the ConsoleHost is opened on the work computer. Additionally, it serves to copy the profile script from that same Dropbox folder, when the ConsoleHost is opened on the home computer. Keep in mind that I’ve purposely written this function to always go from the work computer, to the home computer. I don’t plan to make changes in the profile script on the home computer, that I’ll then expect or want, on the work computer.

Let’s cover what’s happening in this function. In lines 1 – 11 we define out Sync-ProfileScript function with two parameters: ComputerType and LocalProfileScriptPath. We use the ComputerType parameter to indicate whether this is the source or destination computer. Source is work and destination is home. The LocalProfileScriptPath parameter points to the profile script (.ps1 file), located in Dropbox. As it has a default value, I don’t have to send in a parameter value when the function is invoked.

Our Begin block serves one quick purpose and that’s to exit the function immediately, if we’re not in the ConsoleHost, and instead inside a host such as the ISE. The magic happens in the Process block. Here’s what we do, in order: (1) Test to see that there’s an actual profile script being used, (2) if there is, compare the LastWriteTime on the profile script file used by the ConsoleHost ($PROFILE), and the file in Dropbox, (3) if they are different, either copy the profile script used by the ConsoleHost to Dropbox, or copy the profile script in Dropbox to the location used by the ConsoleHost. Again, this is dependent on which computer we’re using: work or home.

Let’s stop and consider something from the perspective of the home computer. If I open the PowerShell ConsoleHost there, it’ll potentially download the newest version of the profile script from Dropbox to its place on the filesystem. Great. The problem is that any changes to the profile script won’t be usable until the next time that ConsoleHost is started. I think I could’ve run & $PROFILE, but I skipped that option as I vaguely remember that it didn’t always work for me.

Therefore, I added a bit more code. If the home computer notices a change to the profile script, it’ll indicate that to the user by writing “Profile Script has been updated. Restart ConsoleHost?” and “Enter r to Restart or c to Cancel.” If the user enters “r,” it’ll restart the ConsoleHost loading the newest version of the profile script by default. If the user enters “c,” it’ll cancel the ConsoleHost restart and the newest version of the profile script will not be updated on the home computer. It will, however, be updated the next time a ConsoleHost is opened.

Now, on to the final portion of my profile script. Don’t worry, this part is less involved than the Sync-ProfileScript function.

# Determine if sync counter variable exists.
If (-Not($env:SyncProfileCounter)) {
    $env:SyncProfileCounter = 0

    # Copy profile script to/from Dropbox by calling Sync-ProfileScript.
    If ($env:COMPUTERNAME -eq $WorkComputer) {
        Sync-ProfileScript -ComputerType Source

    } ElseIf ($env:COMPUTERNAME -eq $HomeComputer) {
        Sync-ProfileScript -ComputerType Destination
    } # End If-ElseIf.
} # End If.

This section of the profile script invokes the Sync-ProfileScript function we discussed in the last section. If it’s run on the work computer, it indicates to the Sync-ProfileScript function to copy the profile script to Dropbox. If it’s run on the home computer, it indicates to the function to copy the profile script from Dropbox. We know this already, however. It uses a counter variable stored inside an environmental variable to ensure this If statement doesn’t perpetually run, and therefore perpetually call the Sync-ProfileScript function. I won’t a separate post about that here.

I realize that not everyone is using Dropbox. If you’re using another service, then I highly suspect you can use what you’ve learned here, with them as well. You’ll just need to determine the local path to use, and adjust your profile script accordingly. If someone wants to let me know about OneDrive, that would be great. I’d be more than happy to include that information in this post!

Here’s the complete profile script template. Thanks for your time.

$WorkComputer = 'WorkComputer'
$HomeComputer = 'HomeComputer'

Switch ($env:COMPUTERNAME) {

    # Work computer only.
    {$_ -eq $WorkComputer} {

    } # End work computer only.

    # Home computer only.
    {$_ -eq $HomeComputer} {

    } # End home computer only.

    # Work and home computer.
    {$_ -eq $WorkComputer -or $_ -eq $HomeComputer} {
 
    } # End Work and home computer.
} # End Switch.

# Create Sync profile function.
Function Sync-ProfileScript {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateSet('Source','Destination')]
        [string]$ComputerType,

        [Parameter()]
        [string]$LocalProfileScriptPath = "$env:USERPROFILE\Dropbox\PowerShell\Profile\Microsoft.PowerShell_profile.ps1"
    )

    Begin {
        # Exit if NOT the ConsoleHost.
        If (-Not($Host.Name -eq 'ConsoleHost')) {
            break
        }
    } # End Begin.

    Process {
        If (Test-Path -Path $PROFILE) {
            $CompareFiles = Compare-Object -ReferenceObject (Get-Item $PROFILE).LastWriteTime -DifferenceObject (Get-Item $LocalProfileScriptPath).LastWriteTime -IncludeEqual |
                Select-Object -First 1

            If ([System.Boolean]($CompareFiles.SideIndicator -ne '==')) {

                Switch ($ComputerType) {
                    'Source' {Copy-Item -Path $PROFILE -Destination $LocalProfileScriptPath}

                    'Destination' {
                        Copy-Item -Path $LocalProfileScriptPath -Destination $PROFILE

                        'Profile Script has been updated. Restart ConsoleHost?'
                        Do {
                            $Prompt = Read-Host -Prompt 'Enter r to Restart or c to Cancel'
                        } Until ($Prompt -eq 'r' -or $Prompt -eq 'c')

                        If ($Prompt -eq 'r') {
                            Start-Process -FilePath powershell.exe
                            Stop-Process -Id $PID
                        }
                    }
                } # End Switch.
            } # End If.
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Sync-ProfileScript.

# Determine if sync counter variable exists.
If (-Not($env:SyncProfileCounter)) {
    $env:SyncProfileCounter = 0

    # Copy profile script to/from Dropbox by calling Sync-ProfileScript.
    If ($env:COMPUTERNAME -eq $WorkComputer) {
        Sync-ProfileScript -ComputerType Source

    } ElseIf ($env:COMPUTERNAME -eq $HomeComputer) {
        Sync-ProfileScript -ComputerType Destination
    } # End If-ElseIf.
} # End If.

TechNet Wiki Link:
https://social.technet.microsoft.com/wiki/contents/articles/37104.powershell-sync-profile-script-from-work-to-home.aspx

February 2017 Guru Link:
https://social.technet.microsoft.com/wiki/contents/articles/36936.technet-guru-competitions-february-2017.aspx

ConsoleHost to ConsoleHost Variable

I often do things, or learn things, in PowerShell that lead me in the direction of writing a new post. Sometimes it’s just experimenting, and sometimes it’s a part of a bigger project. Well, the latter is why we are here today. A current profile script project of mine — that I’ll post as soon as possible — requires that a new ConsoleHost (that’s the standard blue console screen; not the ISE), open and then the current one close. That part is easy.

The problem is that my new ConsoleHost needs to know if a variable has been set, or not, in the previous ConsoleHost. Here, follow along. Start by opening a new ConsoleHost. Inside that host program, create a variable and assign it some sort of value, as I’ve done in the below example.

PS > $Variable = 'This is my standard variable.'
PS > $Variable
This is my standard variable.

In my above example, I tested that my variable had been properly assigned by returning its value. So far, so good. Now, we’ll run a command to (1) open a ConsoleHost from this ConsoleHost, and (2) exit the original ConsoleHost. The semi-colon is a command separator. It allows me to put two commands on the same line and have them run in succession with a single press of the Enter key. One ConsoleHost closes and a new one opens.

PS > Start-Process -FilePath powershell.exe; exit

Now that we have a new ConsoleHost and our old one has exited, let’s see if our variable is waiting for us.

PS > $Variable
PS > 

Nope. That variable was created in a different PowerShell session, and so there’s no getting it back. It simply doesn’t exist any longer. Without fully thinking it through, I briefly thought to use a globally scoped variable instead. It wasn’t long at all, before I realized what a foolish idea that had been. My ConsoleHost was the global scope. With another ConsoleHost, all I’d have is another, separate global scope. Not helpful. I considered writing the variable to disk, but luckily, I didn’t have to consider that option for long either. I had a better idea; I was going to use an environmental variable. Follow along.

PS > $env:Variable = 'This is my standard, environmental variable.'
PS > $env:Variable
This is my standard, environmental variable.
PS > Start-Process -FilePath powershell.exe; exit
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS > # This is a new PowerShell ConsoleHost.
PS > $env:Variable
This is my standard, environmental variable.
PS > ise
PS > # This is in the PowerShell ISE Host.
PS > $env:Variable
This is my standard, environmental variable.

So, as we’ve seen, the environmental variable will follow us along from ConsoleHost to ConsoleHost to the ISE too, providing we open each of them from an existing host that includes the environment variable. Oh, and if you were wondering, the variable is still with us if we launch a ConsoleHost from the ISE (there’s a button for that). The environmental variable option works beautifully for my upcoming profile script project. I’ll link that from here when it’s up, and you’ll already understand that piece of it!

So it’s been included, the environmental variables are presented as a drive by PowerShell. The following command will show all of these variables. If you see some in there that you can use, then do so. It’s always better to use $env:COMPUTERNAME, than it is to hard code a computer name inside your functions.

Get-ChildItem -Path env:

Name                           Value
----                           -----
ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\tommymaynard\AppData\Roaming
CommonProgramFiles             C:\Program Files\Common Files
CommonProgramFiles(x86)        C:\Program Files (x86)\Common Files
CommonProgramW6432             C:\Program Files\Common Files
...
USERNAME                       tommymaynard
USERPROFILE                    C:\Users\tommymaynard
Variable                       This is my standard, environmental variable.
windir                         C:\Windows

Okay, now we’re really done with today’s post, and oh, hey look, there’s our $env:Variable variable again.

Run Background Commands after Every Command

For a complete introduction to this post, please read the first two paragraphs at PowerShell.org: http://powershell.org/wp/2015/10/12/run-background-commands-after-every-command.

There’s a part II, now. Be sure to read that when you’re done here: http://tommymaynard.com/quick-learn-run-background-commands-after-every-command-part-ii-2015.

You know the prompt, it often looks like this: PS C:\> or this: PS>, or even this [DC05]: PS C:\Users\tommymaynard\Documents>, when you’re interactively remoting to another computer. That built-in function determines the appearance of your prompt. I’ve seen several different modifications of the prompt. You can add the the date and time to the prompt, or like the example in the about_Prompt help file, you can add ‘Hello World’ every time the prompt is displayed. Doable, yes, but helpful, probably not.

This post isn’t about changing the prompt’s appearance, but instead about doing something else inside the prompt function. What the prompt looks like is defined by a function called, prompt — you guessed it. Take a look at your current prompt, as in the example below.

PS> (Get-ChildItem -Path Function:\prompt).ScriptBlock
"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
# .Link
# http://go.microsoft.com/fwlink/?LinkID=225750
# .ExternalHelp System.Management.Automation.dll-help.xml

This is the standard prompt function beginning in PowerShell 3.0 up though PowerShell 5.0, the current version of PowerShell, as of this writing.

This function is invoked each time a command is run and the prompt is displayed again. What if we added additional code inside this function? It would also be run every time a new prompt was displayed. My initial idea was to add the current directory to the WindowTitle. If you don’t know what the WindowTitle is, it’s the text at the top of the console host that says “Administrator: Windows PowerShell,” or “Windows PowerShell,” when in a non-elevated host.

I tried the current directory and quickly realized it took more time to look up at the WindowTitle, from the current prompt, than to leave the default prompt alone and get this information from there. The next idea was to add the date and time to the WindowTitle. This way I would know the time the last prompt was displayed. This is practically the time the last command ended. It seemed useful… for about a minute. I finally decided on putting an indicator in the WindowTitle so that I would know if there were any background jobs or not. I seem to open and close new consoles all day long, and knowing if I was about to dump a console with an active job, whether it was still running or not, or whether it still had data, seemed useful.

Let’s walk though what I did to get this to happen. Before we do that, let’s compare the two images below. The first one shows the standard WindowTitle when there’s no job, and the [Job] indicator when there is a job. We’re going to add a few lines of PowerShell to make this happen.

run-background-commands-after-every-command01run-background-commands-after-every-command02

The first thing I did was to define a function in my profile script ($PROFILE). This function would overwrite the default prompt function, as PowerShell reads in the profile script, after it has already created the default prompt.

Function prompt {

}

Next, I enter the default, prompt text. Notice I’m not changing how the prompt is displayed.

Function Prompt {
    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

Following this, I added the first piece of conditional logic. We test for a variable called $OriginalTitle. This variable will not exist the first time the prompt function is run, as the variable is created inside this If statement. It effectively ends up holding whatever was in WindowTitle when the console host was first opened. You’ll soon see how we reuse this value.

Before we go on, I should mention that I’ve made this a globally-scoped variable. This is because the variable needs to retain its value outside the function, and it needs to exist each time the function ends. Because of the way scope works, when we enter the function the second time and it can’t find $OriginalText, the function will go up a level and check for the existence of the variable in the parent scope, which in this case is the global scope.

Function Prompt {
    If (-Not($OriginalTitle)) {
        $Global:OriginalTitle = $Host.UI.RawUI.WindowTitle
    }

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

In the next part of the function, I added a check to see if the Get-Job cmdlet returns anything. If it does, there are current background jobs, and if it doesn’t, then there are no current background jobs. We’ll start with what happens when there aren’t any jobs, first. In the Else portion of this If-Else statement, we set the current WindowTitle to whatever is stored in the $OriginalTitle variable. This ensures that the WindowTitle looks just like it did when we initially started the console host and there were no background jobs.

Function Prompt {
    If (-Not($OriginalTitle)) {
        $Global:OriginalTitle = $Host.UI.RawUI.WindowTitle
    }

    If (Get-Job) {
        # Coming
    } Else {
        $Host.UI.RawUI.WindowTitle = $OriginalTitle
    }

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

So what happens when there are jobs? In this final portion of the function, we have an embedded If statement. If the current WindowTitle doesn’t already include the string [Job], then we add it. If it’s already there, then we leave it alone, and write the prompt.

Function Prompt {
    If (-Not($OriginalTitle)) {
        $Global:OriginalTitle = $Host.UI.RawUI.WindowTitle
    }

    If (Get-Job) {
        If ($Host.UI.RawUI.WindowTitle -NotLike '[Job]*') {
            $Host.UI.RawUI.WindowTitle = "[Job] $OriginalTitle"
        }
    } Else {
        $Host.UI.RawUI.WindowTitle = $OriginalTitle
    }

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

Thanks to the prompt function, we have another way to do something the moment the console is opened, and now, every time a new command is entered and a new prompt is displayed in the host. This little project has already got me wondering about some other things I may want to try with this function. I might expand what I’ve done so far, and provide indicators when there’s more than one job ([Job] vs. [Jobs]), if the jobs are still running, and perhaps if they have more data (whether it hasn’t been received yet, or was kept when it was received).

Keep in mind that this function runs after every command, successful or otherwise. Do your best not to overload the actions in the function. The default prompt comes in under a millisecond. My prompt, with the code above added, comes in at an average of 1 millisecond. If you end up doing too much in the function, you might end up waiting longer than you’re used to, making your additions to the function questionable.

Have fun, and thanks to everyone for reading this post.

Script Sharing – Functions and Code from My Profile

Over the last couple of years, my profile ($PROFILE) has filled up with a bunch of little functions that I use to help shave seconds off my day. I’ve shared a few in other posts, but here’s a few more that I’ve yet to share, that might be interesting. As a forewarning, in many of these, I understand that there may be a better way to do things.

This first one uses Google to show me the definition of a word. It isn’t written to return results to the Windows PowerShell console, but instead will launch the definition in a web browser. It was good enough at the time, and still is on a occasion (when I don’t actually know what a word means ;)) .

Set-Alias -Name lookup -Value Get-Definition
Function Get-Definition {
    Param (
        [Parameter(Mandatory = $true)]
        [string]$Word
    )
        Start-Process "https://www.google.com/?gws_rd=ssl#q=define:$Word"
}

Here’s an example I just used today, when I read Don Jones’ post about Jeffrey Snover’s promotion to Technical Fellow: http://donjones.com/2015/09/02/congratulations-jsnover-a-well-earned-honor.

Functions and Code from my Profile ($PROFILE)01

And, because why not, here’s a photo of Jeffrey Snover and me. For those of you new to PowerShell, I’m on the right; Snover, PowerShell’s inventor, is on the left. Thanks to PowerShell.org and Will for the photo.

Me and Snover (front)

This next inclusion isn’t a function, but has turned out to be quite helpful. I recently posted it on Twitter. By entering the alias “snip” into the console, I’m able to quickly open the snipping tool. In fact, I just used the alias to grab the screen capture, for the Get-Definition function, above.

Set-Alias -Name snip -Value "$env:SystemRoot\system32\SnippingTool.exe"

Here’s a screen capture of the Snipping Tool program.

Functions-and-Code-from-my-Profile-PROFILE02

This next one is also not a function, but extremely helpful, and likely to be some of the oldest code in my profile. This, while quite small, saves me all the time. Here’s how it works: When the PowerShell console opens, it checks the title bar — the WindowsTitle — for any left or right square brackets: this [, or this ]. If it doesn’t find them, it then modifies the WindowsTitle to include my computer’s name inside square brackets. It is extremely handy to be able to quickly verify that I’m typing in to the console on my machine and not inside a console on a RDP session. Yes, I still RDP for some things, and yes, I will also, at times, open a PowerShell console inside the RDP session (as strange as that may be).

# Set Console Window Title
If (-not($Host.UI.RawUI.WindowTitle -like "*`[*`]*")) {
    $Host.UI.RawUI.WindowTitle += " [$($env:COMPUTERNAME)]"
}

Since I’ll occasionally take and upload screen captures (see the first example, above), I wanted a quick way to remove my edited WindowsTitle, so it didn’t show my computer’s name. That gave way to this micro function to return the WindowsTitle to its default (yes, it’s hard coded) — my console is always elevated (however, the log on to my computer is not). I suppose what I should do, is assign the default text, when the console is first opened, and before the WindowsTitle is changed, into a variable for later use in this function.

# Set Default Console Window Title
Function Set-WindowTitleDefault {
    $Host.UI.RawUI.WindowTitle = 'Administrator: Windows PowerShell'
}

The last function I’ll share today is called Get-PSRemotingSession and it allows me to see if anyone is actively using PowerShell Remoting on a specific computer, or computers. It’s basically a wrapper function for Get-WSManInstance (without having to remember the required parameters).

Function Get-PSRemotingSession([string[]]$ComputerName) {
    Foreach ($C in $ComputerName) {
        Get-WSManInstance -ComputerName $C -ResourceURI Shell -Enumerate | Select-Object -Property Owner,ClientIP
    }
}

That’s all I’ve got for today. I hope that some of these might have been helpful enough to incorporate into your profile. If there’s things you can’t live without in your profile, that you want to share, then comment below, or share them on Twitter.

Update: The last function I introduced, Get-PSRemotingSession, has been updated. Only returning the Owner and ClientIP was fine when I only ran it against a single computer. The problem, when I ran it against multiple computers, is that I didn’t know the name of the computer that had the remote session — it wasn’t included in the output. Therefore, I updated the function, as seen below, so that it’ll indicate the computer name. It can’t get the computer name as part of what’s returned from Get-WSManInstance (without resolving the ClientIP), but it can based on which computer it’s checking to begin with (the value in $C). Here’s the updated version:

Function Get-PSRemotingSession([string[]]$ComputerName) {
    Foreach ($C in $ComputerName) {
        Get-WSManInstance -ComputerName $C -ResourceURI Shell -Enumerate | Select-Object -Property @{N='ComputerName';E={$C}},Owner,ClientIP
    }
}