Read-Host Prompt inside Hash Table

I’m working with Plaster and in doing so, have a few hash tables in my workspace. Suddenly, and out of nowhere, I had a thought: Can I put a Read-Host command, as the value of a key-value pair inside of a hash table? Yes, but before we get too far into this, here’s a simple hash table example without any Read-Host commands.

$Params = @{
    meal = 'lunch'
    food = 'Ramen'
    drink = 'Tea'
}
$Params

Name                           Value
----                           -----
drink                          Tea
food                           Ramen
meal                           lunch

Okay, that was simple: Choose a variable name, prefix it with a dollar sign, create the hash table structure as @{}, and then add key-value pairs as <key> = <value>.

Now, on to a Read-Host example. Read-Host is a cmdlet that can be used to interactively prompt a user for a value. It’s not always preferred, or the right to do, but there are times when it can be useful, and where its use is acceptable. In the next example, we’ll create a single key-value pair ourselves, then we’ll prompt the user for a value to assign to the Number key. As you will also determine, when prompted, the word “five” is entered, and sure enough, the entry ends up as the value for the Number key in our hash table.

Remove-Variable -Name Params
$Params = @{
    Color = 'red'
    Number = Read-Host -Prompt 'Enter spelled out number'
}
$Params
Enter spelled out number: five

Name                           Value
----                           -----
Number                         five
Color                          red

This example was one of those “can I do this moments?” Sure, I was able to return the $Params hash table with the correct value in the Number key after having been prompted for that value. This occurred, as the hash table was being defined and built. I didn’t even know if that would work, but now we both know that it does.

In the next example, we try something even more obscure, but as suspected, it doesn’t work. We first assign the Random key the value of a Read-Host prompt. As we’ve seen, that does work. Next, while still creating keys and values, we take the value assigned to the Random key in the $Params hash table and attempt to assign it to the Number key, as well. Again, this doesn’t work. As suspected, we can’t use the $Params hash table until it’s done being created. It’s not a shock, but it was worth a try, of course.

Remove-Variable -Name Params
$Params = @{
    DayOfWeek = 'Friday'
    Random = Read-Host -Prompt 'Enter spelled out number'
    Number = $Params.Random
}
$Params
Enter spelled out number: three

Name                           Value                                                                                                                             
----                           -----                                                                                                                             
Random                         three
Number
DayOfWeek                      Friday

Even if this did work, I’m not sure how this would ever really serve a purpose, but it’s always worth trying something even if doesn’t seem plausible. And if it did work, I don’t expect that we’d even need duplicate values in different keys anyway, and that’s all we’d be doing. But all that said, we absolutely can use Read-Host to assign a value to a key in a hash table, as it’s being created.

Alright, that was it. Enjoy the week!

It’s Post 300

It was mid 2014 when I decided I was going to read through the about help files and rewrite them, right here on this website. What a dumb idea.

I did a couple and then, as if the world needed to tell me something, I read a Tweet from Jeffery Hicks. I can’t quote it verbatim, but it was along the lines of don’t rewrite help files; they already exist for a reason. I’ve never thought about it until now, but just maybe Jeffery was speaking to me without using my name. Lucky for me, I only completed those two, and not anywhere near the full list of which I’ve included the first five below.

PS > Get-Help -Name about_*

Name                              Category  Module Synopsis
----                              --------  ------  --------
about_ActivityCommonParameters    HelpFile          Describes the parameters that Windows PowerShell
about_Aliases                     HelpFile          Describes how to use alternate names for cmdle...
about_Arithmetic_Operators        HelpFile          Describes the operators that perform arithmeti...
about_Arrays                      HelpFile          Describes arrays, which are data structures de...
about_Assignment_Operators        HelpFile          Describes how to use operators to assign value...
...

On my machine right now, there’s 148 of them! I wouldn’t have even been able to get to 300 if that’s all I wrote about. That’s if I would’ve even gotten that far. I’m glad I stopped, as I don’t believe I would’ve learned as much, had as good of a time, and become a resource for people looking for answers to their PowerShell questions.

PS > (Get-Help -Name about_* | Measure-Object).Count
148

I started this endeavor in June 2014 and now, it’s August 2018. That’s four years and two months, or 50 months. At 300 posts, that’s exactly an average 6 posts per month, or one a week with a couple extra jammed into the same month, each month.

I still love PowerShell. I’m still often rewarded by deciding to take my time to experiment with it. Often I take my experimentation straight to a blog post, in the moment I realized that someone else, would benefit from what I just learned. I’m grateful I took a chance at it, and I’m not done. I’m sure I’ll come up with more to write about and more that I will desire to share with the PowerShell community. And with any luck, it won’t be an about topic rewrite.

Thanks to everyone that’s stopped by over the years.

– Tommy Maynard

Show-PSDriveMenu Revisited


Welcome to the 299th post on tommymaynard.com. It’s countdown to 300!


Back on August 14, 2018, I introduced a new utility I had written and shared via the PowerShell Gallery. It was called Show-PSDriveMenu and here’s a quick example of its usage. It lists the currently available drives, includes a marker to indicate the current drive, and allows you to change drives by entering a corresponding drive number.

The problem with the 1.0 version is that when Show-PSDriveMenu was invoked the initial time, it wouldn’t include the marker to indicate the current drive. Well, now it’s included on all the invocations, to include the very first one.

The updated script be downloaded from the PowerShell Gallery using the Install-Script command, included in the PowerShellGet module, using the below example. Be sure to include the Force parameter if you installed the 1.0 version, in order that the older version is replaced by the newer version.

PS > Install-Script -Name Show-PSDriveMenu -Scope CurrentUser

As mentioned in the original post, if you want this function (downloaded as a script file from the PowerShell Gallery), to load each time you start PowerShell, then add one of the two lines of code below to your profile script. The first is for Windows PowerShell (version 5.1 and earlier), and the second is for PowerShell Core (version 6.0 and later).

. $env:USERPROFILE\Documents\WindowsPowerShell\Scripts\Show-PSDriveMenu.ps1
. $env:USERPROFILE\Documents\PowerShell\Scripts\Show-PSDriveMenu.ps1

Array (Not Hash Table) Splatting


Welcome to the 298th post on tommymaynard.com. It’s countdown to 300!


I’ve been interested… okay fine, consumed with PowerShell for five, maybe six years. At some point along the way, I learned how to splat. That’s where you create a hash table of keys and values (think parameters and parameter values), and include those when invoking a cmdlet or function. Here’s an example of not splatting, and splatting, a hash table.

Get-Process -Name firefox -FileVersionInfo -OutVariable FirefoxProcess

The above example shows how we might typically write this command. The below example uses splatting. To be more specific, it uses hash table splatting.

$Params = @{
    Name = 'firefox'
    FileVersionInfo = $true
    OutVariable = 'FirefoxProcess'
}
Get-Process @Params

Well today, many years since I started this journey, I find myself suddenly exposed to array splatting. Huh? You might be able to guess how this works. We’re still going to issue a command, and we’re still going to use the at sign (@) in front of our variable name, but this time, our variable is going to contain an array, not a hash table. Let’s take a look at a few more examples.

Get-ChildItem -Path '.\Documents\test\' -Filter '*.rtf'
Get-ChildItem '.\Documents\test\' '*.rtf'

In the first above Get-ChildItem example, you can see how we might typically issue this command. In the example below that one, we’re running the same above command; however, we’re doing so without including the Path and Filter parameter names. Because this still works, we know these two parameters are positional (or because we read the help first). This means they can be used correctly without the need to include their matching parameter names. Because they’re positional, we can use array splatting. Take a look at the final below example.

$Params = '.\Documents\test\','*.rtf'
Get-ChildItem @Params

Just a quick reminder. We should always use parameter names in code we expect to been seen, or used, more than once, such as code inside functions and scripts. It’s good to know about this, but it might be something to avoid. Using hash table splatting may still be the way to go, as it keeps our parameter names around.

Making the PowerShell.org ICYMI List


Welcome to the 297th post on tommymaynard.com. It’s countdown to 300!


Recently, PowerShell.org started a new weekly section on their website. It’s ICYMI, also know as, In Case You Missed It. The idea is to gather stories, blogs, and news about the PowerShell community to include in a once-a-week blog post, so people can potentially catch up on the things they might’ve missed during the current week.

A couple times now, I’ve seen my posts end up there! Apparently, I’ve done and shared a couple things that have landed in ICYMI on PowerShell.org. That’s kind of a neat honor. In order to keep these together, so I don’t have to chase them down later, if I find a reason I need them, I thought I write a quick post and catalog these on my own site. So here we go; we’ll do newest to oldest, and hopefully, these won’t be the only two. As of this writing, the newest entry is from August 17, 2018.

If you have something to share with the community, no matter how big or small, find a way to share it; get it out there for the community.

August 17, 2018

https://powershell.org/2018/08/17/icymi-powershell-week-of-17-august-2018/

July 13, 2018

https://powershell.org/2018/07/13/what-you-missed-this-week-in-powershell/

 

Wrapper Function with Pause


Welcome to the 296th post on tommymaynard.com. It’s countdown to 300!


I’ve recently written a wrapper function. Its purpose is to allow a user the ability to invoke four different functions, by only invoking one — the wrapper. As of part of this assignment, I wrote out a quick function to do some testing. I wanted to determine whether or not I’ll allow the wrapper function the ability to pause between the invocation of each wrapped function. At this point, I think that will be included.

So it’s written down somewhere, here’s what I quickly jotted to test this scenario.

Function Test-Pause {
    [CmdletBinding()]
    Param (
        [switch]$Pause
    )

    'First String'
    If ($Pause) {Pause}
    'Second String'
    If ($Pause) {Pause}
    'Third String'
    If ($Pause) {Pause}
    'Fourth String'
}

If the Pause parameter isn’t used, the function never stops. In my test, it just works top to bottom, echoing out each string stored inside the Test-Pause function. That works as expected!

PS > Test-Pause
First String
Second String
Third String
Fourth String

If the Pause parameter is included, however, it will pause after each time a string is written. Once the user presses Enter, it continues until it’s required to pause again, providing it is.

PS > Test-Pause -Pause
First String
Press Enter to continue...:
Second String
Press Enter to continue...:
Third String
Press Enter to continue...:
Fourth String

That was it. I just wanted a place to store this, just in case I find myself in a situation where it might be useful again outside my newest function. Just remember, if you do something like this in an automated fashion, don’t include the Pause parameter: It’s going to require a human that way.

It’s Available: Show-PSDriveMenu


Welcome to the 295th post on tommymaynard.com. It’s countdown to 300!


Update: There’s a updated post here.

It was only yesterday that I shared something I was writing in PowerShell. It was my take on Get-PSDrive that included a built-in way to change locations, within the file system, by entering the number that corresponds to a drive location. Take a look at the Tweet (and yes, I’m aware of my misspelling).

There was definitely some interest on Twitter. Because of that, I set out to correct at least a couple errors that needed a fix. For now, it’s up and available on the PowerShell Gallery at https://www.powershellgallery.com/packages/Show-PSDriveMenu/1.0/DisplayScript. Use the PowerShellGet, Install-Script function to install it.

PS > Install-Script -Name Show-PSDriveMenu -Scope CurrentUser

Here’s an image from the ConsoleHost. There’s a small visual change that’s been added since the above Tweet. Numeric values with one digit (1-9) have an extra space in front of the digit in order that everything lines up well.

Update: It appears that there may be a problem at first launch, as the marker that indicates the current drive (>), doesn’t appear. It does, however, on subsequent uses. I’ll have to take a look at that at some point and see if it can be isolated and resolved. If you catch it (and, seriously there’s a minimal amount of code in this function), feel free to let me know.

Update: If you want this function (downloaded as a script file from the PowerShell Gallery), to load each time you start PowerShell, then add one of the two lines of code below. The first is for Windows PowerShell (version 5.1 and earlier), and the second is for PowerShell Core (version 6.0 and later). You know, when Windows PowerShell and PowerShell began to mean different things.

. $env:USERPROFILE\Documents\WindowsPowerShell\Scripts\Show-PSDriveMenu.ps1
. $env:USERPROFILE\Documents\PowerShell\Scripts\Show-PSDriveMenu.ps1

Single Run Functions


Welcome to the 294th post on tommymaynard.com. It’s countdown to 300!


Up until yesterday, this was potentially the longest I’d gone without writing. Here’s what happened. Life. No really, it was a combination of wrapping up my part in The PowerShell Conference Book, which, if I might add, has done really well. Thus far it’s funded two scholarships — that’s impressive!

The other half of this combination, is that I started two posts that I just haven’t gotten back to work on. They’re fairly involved. That, and a few projects at work, have taken it out of me in the last couple weeks. You know the nights; you don’t dare turn on a computer. But, as I was writing what you’re read so far this evening, I did think of something to share.

At times you may find yourself only needing, or wanting, certain functions in a module to be executed once. Say you’re building out a new Active Directory Domain; there’s likely tasks that should only be completed a single time. But, how does one handle that?

There’s a couple different ways I can think of off the top of my head. We’ll assume we’re on Windows, and for this post, we’ll take advantage of the Windows Registry. While we can check to see if the thing(s) the functions set out to change, were actually changed, or potentially execute a Pester test, for this post, we’re going to cheat. This may not be the best idea, but here we are, a touch desperate for some new content.

Let’s say the below function, Show-String, is a part of the StringModule. Its only purpose is to write a string to the PowerShell host program — the screen. While this isn’t something you’d likely only want to run once, it will suffice for this demonstration. The Begin block will create a Registry Key called StringModule inside HKLM:\SOFTWARE if it doesn’t already exist.

If it does exist, it’ll check for a value named after the function. If the value “Show-String” exists and holds the Data value of “Complete,” it will exit the function and indicate to the user that the function cannot be run more than once. If “Show-String” exists and holds a Data value other than “Complete,” it’ll exit the function as well; however, it’ll indicate to the user that this function may have been run before. This is based on the Name being correct, but that the Data value being incorrect. That shouldn’t happen, but, why not make this quick check and verification.

Function Show-String {
    Param (
        [Parameter()]
        [string]$String
    )

    Begin {
        $CmdName = "$($MyInvocation.MyCommand.Name)"
        $RegistryPath = 'HKLM:\SOFTWARE\StringModule'

        If (-Not(Get-Item -Path $RegistryPath -ErrorAction SilentlyContinue)) {
            New-Item -Path $RegistryPath | Out-Null

        } Else {
            If ([System.Boolean](Get-ItemProperty -Path $RegistryPath)) {

                If ((Get-ItemProperty -Path $RegistryPath).$CmdName -eq 'Complete') {
                    Write-Warning -Message "The $CmdName function has already been run (Name and Data verified)."
                    break

                } ElseIf ((Get-ItemProperty -Path $RegistryPath).$CmdName -ne 'Complete') {
                    Write-Warning -Message "The $CmdName function may have already been run (Name correct but Data incorrect)."
                    break

                } # End If-ElseIf.
            } # End If.
        } # End If-Else
    } # End Begin.

    Process {
        $String
    } # End Process.

    End {
        New-ItemProperty -Path $RegistryPath -Name $CmdName -Value 'Complete' | Out-Null
        If (-Not(Get-ItemProperty -Path $RegistryPath).$CmdName) {
            Write-Warning -Message "Unable to correctly set Registry so function isn't run more than once."
            return
        } # End If.
    } # End End.
} # End Function: Show-String.

Below we have three different runs of the above function, one right after the next. In the first run, the string is displayed. Additionally, although we don’t see it ourselves, our Registry Key is created, as is the Show-String value containing the string data of “Complete.” In the second run, we receive a warning that indicates this function has already been run. Again, it’s checking the Registry. It knows this at 100%, because the value contains the “Show-String” Name and string data of “Complete.” Just before the third run, I edited the value in Data, so that it was something other than Complete.

PS C:\> Show-String -String 'testing 123'
testing 123

PS C:\> Show-String -String 'testing 123'
WARNING: The Show-String function has already been run (Name and Data verified).

PS C:\> # Edited the data in the Registry value to something other than "Complete"

PS C:\> Show-String -String 'testing 123'
WARNING: The Show-String function may have already been run (Name correct but Data incorrect).

And in closing, what else do you think this needs? I know. A function that will clean up those Registry entries when you really have a need to rerun your function. I did that for my project, because I know there’s going to be those times during testing.

Finally, I do want to mention that this function is interacting with HKEY_LOCAL_MACHINE. This area of the Registry requires administrative permissions. You may need to alter this code to use HKEY_CURRENT_USER. If you choose to do that, every HKLM in the above script would need to be, you guessed it, changed to HKCU.

An Array of Hash Tables


Welcome to the 293rd post on tommymaynard.com. It’s countdown to 300!


If you’ve ever looked into Pester and TestCases, then maybe you’ve seen what I’m about to mention. While today’s post has nothing to do with Pester, this feature in Pester uses an array of hash tables. Neat, right? While adding a couple New-PSDrive commands to my PowerShell profile, I moved from this first option,…

$Param = @{
    Name = 'IDT'
    Root = '\\tommymaynard.com\Windows\Internal\Users\tommymaynard'
}
New-PSDrive @Param -PSProvider FileSystem | Out-Null

$Param = @{
    Name = 'IDI'
    Root = '\\tommymaynard.com\Windows\Internal'
}
New-PSDrive @Param -PSProvider FileSystem | Out-Null

…to something else.

While the use of splatting is a bit more grown-up than creating long, multi-line wrapping commands, there’s no reason why we should be executing the same command — New-PSDrive, in this instance — outside of a looping construct. That was my thought today, anyway. If we need to invoke the same cmdlet or function more than once, it should only be entered into our code, once. It is possible there’s an exception here, but for the most, I stick to this belief.

But how do I combine multiple hash tables in to looping construct? Well, my solution had everything to do with my quick remembrance of Pester and TestCases. For fun, we’ll link to a quick example from a Pester issue I recently created in GitHub. This offers us a good example. See the third It block in the Pester example in number 4 (“Current Behavior”). Do you see that? It’s an array of hash tables. Perfect example for what I needed.

As we create our hash tables, we need to ensure they’re being added to an array. Then we can loop through our array elements with ForEach, or ForEach-Object. I’ll include examples for both. First, however, let’s create our array of hash tables.

$HashTableArray = @(
    @{
        Name = 'IDT'
        Root = '\\tommymaynard.com\Windows\Internal\Users\tommymaynard'
    },
    @{
        Name = 'IDI'
        Root = '\\tommymaynard.com\Windows\Internal'
    }
)

And now, let’s ensure what we have, is where we want it, and want we want.

PS > $HashTableArray

Name                           Value
----                           -----
Root                           \\tommymaynard.com\Windows\Internal\Users\tommymaynard
Name                           IDT
Root                           \\tommymaynard.com\Windows\Internal
Name                           IDI

As mentioned, there’s a couple way to loop though these. One is using a Foreach construct — which is the first below example — and one is using the ForEach-Object cmdlet. They’re both below, and listed in the respective order in which they were discussed. So yes, if you’re calling the same command outside of looping construct, you need to consider how to do better, if you can. There’s no reason in my instance that New-PSDrive should be in a our code more than once.

Foreach ($HashTable in $HashTableArray) {
    New-PSDrive @HashTable -PSProvider FileSystem | Out-Null
}

$HashTableArray | ForEach-Object {
    New-PSDrive -Name $_.Name -PSProvider FileSystem -Root $_.Root | Out-Null
}

If you didn’t notice, I’ve piped my New-PSDrive commands to Out-Null. This was done in order to remove the output that would’ve otherwise been produced.

Add a Single Space Between Characters


Welcome to the 292nd post on tommymaynard.com. It’s countdown to 300!


Even though we’ve been doing this PowerShell thing awhile, we don’t always write our own solutions. You, like me, just might go looking for someone else’s solution, before spending a few minutes of your own time, writing a solution. I’m guilty. That’s said, there’s nothing wrong with it. Seeing what other people have done is often beneficial to our own solutions. It’s part of why I’ve been blogging since mid 2014; take from me, what helps for you.

There’s a very small function I’m currently writing that needed a feature. That feature, which I couldn’t locate well enough anywhere else, is something I wrote myself. Now that I have my solution, after a small amount of time yesterday, I thought I share it for someone else some day.

I needed to accept a string with or without spaces in it, and ensure each character in the string had a space between it when I was done. This ‘ a bcd ef 12 34 gh i’ needed to be come this ‘a b c d e f 1 2 3 4 g h i’. I opted first, to remove all the spaces, whether there were any or not, and then add a space between each character in the string. In case it’s helpful, I’ve include the few lines of PowerShell I wrote out to get this to suitably work for me. Have a look, and take it if you want it.

Remove-Variable -Name String,NewString -ErrorAction SilentlyContinue; Clear-Host

$String =  ' a          ll the d                ogs are so l o u d 1     2 3'
$String
Pause

$String = $String.Replace(' ','')
$String
Pause

$String.ToCharArray() | ForEach-Object {
    $NewString += "$_ "
}
$NewString

Now, let’s run it.

 a          ll the d                ogs are so l o u d 1     2 3
Press Enter to continue...:
allthedogsaresoloud123
Press Enter to continue...:
a l l t h e d o g s a r e s o l o u d 1 2 3

Perhaps you can tell what I might’ve been dealing with when I was writing this. I was at home. My wife was hosting a Girl Scout get together, and the dogs were being just a touch annoying.