Tag Archives: Get-Random

Testing Multiplication Facts

Edit: I said I would not, but I added more to the final function at the bottom of this post.

My daughter entered the office right on time — the end of my workday yesterday — and asked me to give her math problems. That means multiplication facts. While she is all but done learning her multiplication tables these days, I guess we are still practicing. It was my job to randomly choose two numbers between 1 and 12 that she would multiply in her head and say the answer out loud. I was sitting an arm’s length away from Windows Terminal and PowerShell, so I was about to give up coming up with the two numbers myself. PowerShell was going to do this for me.

This is not the first time I have discussed multiplication this school year. Here is a related post. This post is not really a part 2.

I didn’t want to spend much of our time writing PowerShell, so I quickly wrote the below PowerShell and we started. Because she could see the problems in the console — I zoomed in like I never zoomed in before — we were able to burn through at least a hundred, maybe two before we wrapped up our  “give me math” session.

Clear-Host; "$(1..12 | Get-Random) x $(1..12 | Get-Random)"

Here is a quick video that is probably worth seeing at this point in the post.

 

As we went through these random multiplication problems, my mind kept going to how I would have written this had I had more time to prepare. Well, that is what this post is for now. I know I can add a ridiculous amount of features, sure, but I have other things to do, so I am going to keep the features to a minimum. It did dawn on me to do something like my 1 to 100 game, but again, I only want to give her enough to play it herself. I did not need to keep score or be as nearly as polished as that one.

$1st = Get-Random -InputObject (1..12)
$2nd = Get-Random -InputObject (1..12)
"$1st x $2nd = $($1st * $2nd)"

3 x 8 = 24

The above example shows some changes to what I had authored previously. Basically, I can create a multiplication problem and include the answer too. This is not very helpful, as my daughter would be able to see the answer. Either way, this was a logical step toward the next example.

In this example, we add a do-until loop, hiding the product from the user. The Read-Host prompt will stop promoting for the answer as soon as it is entered correctly.

$1st = Get-Random -InputObject (1..12)
$2nd = Get-Random -InputObject (1..12)
"$1st x $2nd = ?"
do {$Product = Read-Host -Prompt 'Enter Product'}
until ($Product -eq $1st * $2nd)

1 x 8 = ?
Enter Product: 8

6 x 8 = ?
Enter Product: 45
Enter Product: 50
Enter Product: 48

And that was it. Plenty more could have been added, however, I am not sure she would even appreciate additional features and the extra work. This PowerShell will be good enough. I bet she will think it is great.

Okay fine, I decided to add a little more, but I am done after this, I swear. I put it in a function with a couple of parameters and here it is. As you will see, not much more time was spent doing this.

Function Test-Multiplication {
    [CmdletBinding()]
    Param (
        [Parameter()]
        $FirstRange = (1..12),

        [Parameter()]
        $SecondRange = (1..12)
    )

    $1st = Get-Random -InputObject $FirstRange
    $2nd = Get-Random -InputObject $SecondRange

    "$1st x $2nd = ?"
    do {$Product = Read-Host -Prompt 'Enter Product'}
    until ($Product -eq $1st * $2nd)
}
Test-Multiplication
9 x 9 = ?
Enter Product: 81

Test-Multiplication
4 x 6 = ?
Enter Product: 21
Enter Product: 24

Last note here. Because we have parameters, we can, if we want, send in different ranges for the FirstRange and SecondRange parameters. Ony want to practice your 4’s and you can do this.

Test-Multiplication -FirstRange (4..4)
   
4 x 1 = ?
Enter Product: 4

Test-Multiplication -FirstRange (4..4)
   
4 x 6 = ?      
Enter Product: 24

Okay, I am done now — for real.

Update: Well, it was not for real. I lasted a night. My daughter played the multiplication game and it was clear it needed one more thing. Even though she, at 10, now knows to press the up arrow for the last command, I went ahead and added a little more. Here is the updated function first and then an example of it being executed.

Function Test-Multiplication {
    [CmdletBinding()]
    Param (
        [Parameter()]
        $FirstRange = (1..12),

        [Parameter()]
        $SecondRange = (1..12)
    )

	
	do {
		$1st = Get-Random -InputObject $FirstRange
		$2nd = Get-Random -InputObject $SecondRange

		"$1st x $2nd = ?"
		do {$Product = Read-Host -Prompt 'Enter Product'}
		until ($Product -eq $1st * $2nd)

		do {
			$Replay = Read-Host -Prompt 'Enter = More (Q = quit)' 
		} until ($Replay -eq '' -or $Replay -eq 'Q')
	}
	until ($Replay-eq 'Q')
}
[PS7.2.1][C:\] Test-Multiplication 
4 x 3 = ?      
Enter Product: 12
Enter = More (Q = quit): 
5 x 10 = ?     
Enter Product: 50
Enter = More (Q = quit): 
4 x 5 = ?      
Enter Product: 20
Enter = More (Q = quit): 
9 x 4 = ?      
Enter Product: 35
Enter Product: 39
Enter Product: 36
Enter = More (Q = quit): q
[PS7.2.1][C:\] 

Okay, I am done again, for now. Ugh.

Skip Process and End Blocks

I started to reply to this recent Tweet on Twitter when I stopped and jumped into a new post here instead. It’s been awhile, but I’ve found a Tweet I couldn’t reply to in the allotted characters allowed by Twitter (these days). This isn’t to say I reply to everything. It does say however, that I’m intrigued by the Twitter, posed question. Here’s that Tweet now. Well, mostly. It was deleted at some point after I started this post, so here’s the text that was that Tweet.

“Is there a fast way to return from Function from within Begin {}
without having to go thru Process/End ? #PowerShell”

— (Name and handle removed) October 16, 2018

My first thought here was, why would Process and End take so long to finish that someone wouldn’t want them to execute? In my world, we put as little code into our functions as possible. I’ve said it multiple times already, but I’d rather have five, 20 line functions, before I had one, 100 line script. This makes troubleshooting so much easier. If I can immediately remove 60, or 80, lines in which to deal with, then I’m all for it. Well, I’m not in charge of everyone’s functions and scripts, so I quickly pushed that thought away. This and that fact that there wasn’t a mention of too much code in the Process and End blocks.

Next, I recalled what I’ve done a few times. In some tooling I’ve written in the past, I’ve created a variable that only when True ($true), would it allow the code to fully enter into the Process/End block. Something like the below example, which uses the same variable to determine whether the blocks, other than the Begin block, are executed.

Here’s how this works: The below Limit-BlockUsageOne function chooses a random number — either 1 or 2 — inside the Begin block. If the numeric value is 1, as stored in $RandomNumber, we will enter the Process and End blocks. If it’s 2, we will only enter the two additional blocks long enough to know we won’t run any additional code from inside those two blocks. We can’t completely avoid them, or skip them, but we can get close.

Clear-Host
Function Limit-BlockUsageOne {
    [CmdletBinding()]
    Param (
    )

    Begin {
        $RandomNumber = Get-Random -Minimum 1 -Maximum 3
    } # End Begin
    Process {
        If ($RandomNumber -eq 1) {
            "Inside the Process block."
        } # End If.
    } # End Process.
    End {
        If ($RandomNumber -eq 1) {
            "Inside the End block."
        } # End If.
    } # End End.
 } # End Function: Limit-BlockUsageOne

1..10 | ForEach-Object {
    "-----Execution # $_-----"
    Limit-BlockUsageOne
}

So again, while we do enter the Process and End blocks, we leave almost instantly when the value stored in the $RandomNumber variable is a 2. Here’s some random results, based on the Get-Random cmdlet, which is used inside the Limit-BlockUsageOne function.

-----Execution # 1-----
-----Execution # 2-----
-----Execution # 3-----
-----Execution # 4-----
Inside the Process block.
Inside the End block.
-----Execution # 5-----
Inside the Process block.
Inside the End block.
-----Execution # 6-----
-----Execution # 7-----
-----Execution # 8-----
Inside the Process block.
Inside the End block.
-----Execution # 9-----
Inside the Process block.
Inside the End block.
-----Execution # 10-----
Inside the Process block.
Inside the End block.

Another thought, because there was at least a couple, was to make use of the break command within the Begin block. Part of me wonders if this option is why the Tweet was deleted — did he figured out the answer to his own question? In this next example, we simply exit the function the moment after we write the “Begin” string. Simple.

Function Limit-BlockUsageTwo {
    Begin {
        'Begin.'
        break
    }
    Process {
        'Process'
    }
    End {
        'End.'
    }
} # End Limit-BlockUsageTwo.

The output of the above function is always going to be the string “Begin”. Because of the break command, we’ll never see the string “Process” or “End” output to the screen. Do keep in mind that we can add some logic to determine whether or not the break command is actually  executed. This, much as we did when we checked for the numeric value of 1 and 2 in the previous example. Do note that this option can in fact completely avoid executing any code inside the Process and End blocks. Based on the Tweet, that’s exactly what the user was after.

While I can’t think of an official way to avoid going through Process or End — you know, like something built in — there’s a couple ways to get there and get close, even if it’s not official.

Randomly Selecting a Name from a CSV File

I have to admit, I’m kind of excited to be doing another Twitter Reply post. This one is the result of a post by Michael Bender. I’ve never met him, but I’ve follow his career a bit in the last few years, when I was introduced to his name via TechEd. He made a recent Tweet where he asked for a PowerShell genius to help build him a script. The goal was to randomly select a name from a CSV file, with an option to choose how many names it should randomly select.

Here’s the Tweet: https://twitter.com/MichaelBender/status/593168496571322370

I wouldn’t consider myself a genius, but if you consider what he’s after — a name from a CSV file (not a line from a text file) — then there’s a better option then the one he accepted: “get-content file.csv | get-random.” This will work, but there’s some reasons why this isn’t such a great idea. We can use the Get-Content cmdlet with CSV files, but we generally don’t. CSV files are created and formatted in such a way, that using Get-Content, instead of Import-Csv, isn’t the best way to do things.

Let’s consider that we’re using the CSV file in the image below. It has three headers, or column names: Name, Age, and HairColor.

Randomly Selecting Name from a CSV File01

First of all, if we use Get-Content, it’s going to consider the first line — the header line, or the column headings — a selectable line within the file. Here’s that example:

PS C:\> Get-Content .\file.csv | Get-Random
Name,Age,HairColor

Uh, not helpful. While this isn’t the end of the world, as we can simply rerun the command (and probably get something different), we’d be better off to write this so that this error is not a possibility — something we’ll see in a moment, when we use Import-Csv.

Second of all, unless the only column in the CSV file is Name, then we’re not returning just a name, but instead, everything in a row of the file. This means that if I randomly choose a line in the file, that it will include information that Michael didn’t request — the age and hair color.

PS C:\> Get-Content .\file.csv | Get-Random
Jeff,19,Brown

I suppose we could start parsing our returned value to just return the name ((Get-Content .\file.csv | Get-Random).Split(‘,’)[0]), but seriously, let’s skip that and use one of the cmdlets that was built to work with CSVs directly. We’ll assume we’re still using the same CSV file in the image above.

PS C:\> Import-Csv .\file.csv | Get-Random | Select-Object Name

Name
----
Lance

Um, that was easy. If we wanted to increase the number of names returned from our CSV file, then we would use Get-Random’s -Count parameter and an integer value, such as in this example:

PS C:\> Import-Csv .\file.csv | Get-Random -Count 3 | Select-Object Name

Name
----
Bob
Stephen
Sue

I think we’re best off when we use the *-Csv cmdlets with CSV files, and the Get-Content cmdlet with most other file types we can read in PowerShell. There is at least one exception of doing this in reverse: Using Import-Csv with a text file, that has a delimiter, might help you better parse the information in your file. Consider this text file:

Randomly Selecting Name from a CSV File02

The next few examples will progressively show you how to use Import-Csv with a delimited text file until all of the “columns” are broken apart. When we’re finished, we can even export our data into a properly formatted CSV file, and then import it, piping those results to other cmdlets — take a look.

PS C:\> Import-Csv .\file.txt

001|Sunday|Car7
---------------
002|Monday|Car2
003|Tuesday|Car3
004|Wednesday|Car3
005|Thursday|Car7
006|Friday|Car2
007|Saturday|Car3

PS C:\> Import-Csv .\file.txt -Header Id,Day,Car

Id                                      Day                                     Car
--                                      ---                                     ---
001|Sunday|Car7
002|Monday|Car2
003|Tuesday|Car3
004|Wednesday|Car3
005|Thursday|Car7
006|Friday|Car2
007|Saturday|Car3

PS C:\> Import-Csv .\file.txt -Header Id,Day,Car -Delimiter '|'

Id                                      Day                                     Car
--                                      ---                                     ---
001                                     Sunday                                  Car7
002                                     Monday                                  Car2
003                                     Tuesday                                 Car3
004                                     Wednesday                               Car3
005                                     Thursday                                Car7
006                                     Friday                                  Car2
007                                     Saturday                                Car3

PS C:\> Import-Csv .\file.txt -Header Id,Day,Car -Delimiter '|' | Export-Csv -Path C:\file-cars.csv -NoTypeInformation
PS C:\> Import-Csv .\file-cars.csv

Id                                      Day                                     Car
--                                      ---                                     ---
001                                     Sunday                                  Car7
002                                     Monday                                  Car2
003                                     Tuesday                                 Car3
004                                     Wednesday                               Car3
005                                     Thursday                                Car7
006                                     Friday                                  Car2
007                                     Saturday                                Car3

PS C:\> Import-Csv .\file-cars.csv | Where-Object Car -eq 'Car3'

Id                                      Day                                     Car
--                                      ---                                     ---
003                                     Tuesday                                 Car3
004                                     Wednesday                               Car3
007                                     Saturday                                Car3

PS C:\> Import-Csv .\file-cars.csv | Where-Object Car -eq 'Car3' | Select-Object Day

Day
---
Tuesday
Wednesday
Saturday

So, if I had seen the Tweet first, I would have to recommend Import-Csv, piped to Get-Random, and then piped to Select-Object Name. Thanks for reading the post.

Implicit Remoting and the Exchange Cmdlets

I work in an environment where we administer Microsoft Exchange 2010 and I’m proud to say that I don’t have the Exchange Management Tools installed on my laptop. Now, that doesn’t mean I don’t sometimes RDP into an Exchange box, but it does force me to forgo that and use Windows PowerShell when I want to do something quicker than to RDP, log on, and open the EMC. One of the first things I added to my $PROFILE, after writing it for use in a script, was a function that would establish a PSSession to one of the Exchange servers. This allows me to run Exchange-specific cmdlets without leaving my laptop and without having the Exchange Management Tools installed.

The first thing I started with was an empty function that I called New-ExchangeSession.

Function New-ExchangeSession {

}

I had a few requirements for this project. One, I didn’t want to rely on connecting to the same Exchange server each time I created a PSSession. If for some reason it wasn’t available, I would have a problem connecting… in addition to a non-responsive Exchange server. Two, I didn’t want to hard code my Exchange server list in either my script (bad!) or in an external file (not as bad, but not great). What I did was make use of the Get-ADGroupMember and the Get-Random cmdlets to return the names of the Exchange Servers from an Active Directory group in which they were all members, and then randomly select one.

I did this as part of Do-Until loop. In line 3, the first thing it does is acquire the members of the group, ExchangeServers, using the Get-ADGroupMember cmdlet included in the Active Directory module. If you’re running PowerShell 3.0 or greater it will load this module automatically. If you’re not, then you’ll have to add it yourself using the Import-Module cmdlet before beginning the Do-Until loop.

Using dotted notation (.Name) we return only the Name property of the group members. Once collected, the Get-Random cmdlet is used to randomly select one of the names and then assign it to the $ExchServ variable.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-Random (Get-ADGroupMember -Identity 'ExchangeServers').Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)
}

If you’re using something older than 3.0, and you really shouldn’t be, you’ll find the dotted notation version just doesn’t work as expected. In that case, you will have to handle this in a more procedural way as seen in the example below. Regardless of which way you get a server name and assign it to the $ExchServ variable, it must reply to a ping, by use of the Test-Connection cmdlet in line 5 above, and line 7 below. This is the conditional check of Do-Until loop. If Test-Connection returns True, it will break out of the Do-Until. If it returns False, it will randomly select another server and try again.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-ADGroupMember -Identity 'ExchangeServers'
        $ExchServ = Get-Random $ExchServ
        $ExchServ = $ExchServ.Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)
}

We start to create the PSSession once we have an Exchange server name chosen and verified it is reachable across the network. This is done by creating a variable, $Session on line 7 below, that will store the session information. We then use that session information as part of our Import-PSSession cmdlet that brings the Exchange cmdlets down to our local computer. The final, informational message on line 9 simply indicates the Exchange server in which we’ve connected.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-Random (Get-ADGroupMember -Identity 'ExchangeServers').Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)

    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchServ.mydomain.com/powershell" -Authentication Kerberos
    Import-PSSession -Session $Session -CommandName * -FormatTypeName * | Out-Null
    Write-Output "Connected to $ExchServ"
}

You will often, if not always, receive a warning about some of the imported commands may have unapproved verbs. To view your active session, use the Get-PSSession cmdlet, and to close the session, use the Remove-PSSession -Id  #, where # equals the Id number returned from the Get-PSSession cmdlet.