Category Archives: Quick Learn

Practical examples of PowerShell concepts gathered from specific projects, forum replies, or general PowerShell use.

PSMonday #8: Monday, June 20, 2016

Topic: Less Used Variable Properties

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

When someone sets, or assigns, a variable in Windows PowerShell, we typically expect it to look a certain way, as is demonstrated below. This example also shows how we typically return a variable’s value, after it’s been assigned.

$String = 'Welcome back to Monday.' # Assign a value to variable.
$String # Return a variable’s value.

Welcome back to Monday.

This assigns, or sets, the variable named string, the value of ‘Welcome back to Monday.’ Straightforward, right? Well, this isn’t the only way to assign a variable in PowerShell. There’s a more formal process that offers a few extra options. When we use the *-Variable cmdlets, we don’t use the dollar sign ($). The dollar sign is only used to indicate to the PowerShell parser that what follows it, will be a variable name. The difference here is that these variable cmdlets already know you’re providing a variable name.

New-Variable -Name String2 -Value 'Come on, Friday.'
Get-Variable -Name String2

Name                           Value
----                           -----
String2                        Come on, Friday.

If you choose to use the Get-Variable cmdlet to return just the value, you can use the -ValueOnly parameter, dotted-notation, or Select-Object’s -ExpandProperty parameter. In older versions of PowerShell, dotted-notation may not be an option.

Get-Variable -Name String2 -ValueOnly
(Get-Variable -Name String2).Value
Get-Variable -Name String2 | Select-Object -ExpandProperty Value

Come on, Friday.
Come on, Friday.
Come on, Friday.

I’m not here to suggest variables should always be created with New-Variable, that values should always be returned with Get-Variable, that variables should always be updated with Set-Variable, or even that we should always clear or remove variables with Clear-Variable and Remove-Variable, respectively. What I’m out to do, is tell you about a couple extra properties that are attached to our variables, that you might not know about, and how we might use them.

Let’s modify the command we used to return the value of our $String2 variable, so we return all the properties. Keep in mind, that we can do the same thing with our $String variable that was created without the New-Variable cmdlet.

Get-Variable -Name String2 | Select-Object *

Name        : String2
Description :
Value       : Come on Friday.
Visibility  : Public
Module      :
ModuleName  :
Options     : None
Attributes  : {}

Notice that we have a Description property and an Options property. The Description property is another way to provide additional meaning to a variable. While you should strive to name your variables in a way that describes their contents, if you’re feeling up to it, you can add additional information about the variable in this property.

Set-Variable -Name String2 -Description 'Demo purposes only'
(Get-Variable -Name String2).Description

Demo purposes only

Let’s talk about the Options property next week, as it’s a bit more useful.

Specify Which Functions Can Invoke Another Function Part II

It wasn’t long after I wrote and posted Part I of this topic — which I didn’t know was a Part I at the time — that I had another idea.

For those that didn’t read the post linked above, I have a function called Send-ErrorMessage. Its purpose is to send an email if the catch block of a try-catch, that doesn’t include an error exception, executes. It’s a safety net, just in case I haven’t properly coded for an unknown error condition. Because I had to export the function, so that it’s available to other functions in other modules, I needed a way to ensure that only specific functions were able to invoke Send-ErrorMessage. I didn’t want a user using the function; I only wanted the functions I wrote, that were approved, to be able to use the Send-ErrorMessage function.

Well today, I took the conditional logic that was taking up space in my Begin block and moved it to the [ValidateScript()] attribute. This also helped clean up some of the Process block, too. The only thing that I’m giving up, is that now the message to someone trying to invoke this function with an unapproved function, or at the prompt, is that it’ll show an error using red text, instead of a warning using orange text (in the ISE).

I may have mentioned this before, but I believe it’s okay to display standard, red text error messages in your tools, depending on the audience. If I’m writing tools for my team, they should be comfortable enough to decipher PowerShell errors. If the tool is being written for end-users, or lower tier employees, then it may be wise to better control the errors and warnings.

The complete, modified code is below. It begins by defining the Send-ErrorMessage function. Then it creates two additional functions — one that is approved to invoke Send-ErrorMessage, and one that’s not. Then, it invokes those two functions — Test-Allowed and Test-NotAllowed, respectively — and displays the results one right after the other. The first function will be permitted and will write the verbose statements in the Process block, but the second function will fail inside the [ValidateScript()] attribute, as it’s not approved to invoke Send-ErrorMessage.

Copy, paste, and take it for a run. After you’ve run the code, enter Send-ErrorMessage manually. It still won’t let you run it from the command line, even if you provide an approved function name, as the value of the -FunctionName parameter. That’s right, you can’t fake it out, as it’s not taking your word, it’s using the value returned by Get-PSCallStack. Thanks for reading!

Function Send-ErrorMessage {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateScript({
            $AcceptableFunctions = 'Get-AJob','Test-Allowed','Get-Real'
            $CallingFunction = (Get-PSCallStack)[1].Command
            If (-Not ($CallingFunction -in $AcceptableFunctions)) {
                Throw "The `"$_`" function or command, does not have approval to invoke this function. Send-ErrorMessage will not work if you use the function manually and enter an approved function name."
            } Else {
                $true
            }
        })]
        [string]$FunctionName
    )

    Begin {
    } # End Begin.

    Process {
        Write-Verbose -Message "The calling function ($($PSBoundParameters.FunctionName)) is approved." -Verbose
        Write-Verbose -Message 'Beginning to send error message ...' -Verbose
    } # End Process.
    
    End {
    } # End End.
} # End Function: Send-ErrorMessage.


Function Test-Allowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage -FunctionName $MyInvocation.MyCommand.Name
    }
} # End Function: Test-Allowed.


Function Test-NotAllowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage -FunctionName $MyInvocation.MyCommand.Name
    }
} # End Function: Test-NotAllowed.


# Invoke Function.
Test-Allowed

# Invoke Function.
Test-NotAllowed

Specify Which Functions Can Invoke Another Function

I have a function called Send-ErrorMessage. Its sole purpose is to send me an email if the catch block of a try-catch statement executes. Now, not just any catch block, as there can be more than one, but one where a specific error exception is not included.

The below example obtains a random Exchange server name as part of its try block. If that command produces an error, one of two catch blocks will execute. The first catch block will execute if the error produces a Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException exception. The exception is listed in square brackets after the catch keyword. The second catch block will execute if it’s any other exception. This, is the error I want to know about. When the code in the second catch block executes, it first creates a parameter hash table and it then sends that to the Send-ErrorMessage function, where the error is presented to the user, and an email is sent.

try {
    # Randomly select Exchange Server.
    $ExchServ = Get-Random (Get-ADGroupMember $ADGroup |
       Where-Object -FilterScript {$_.objectClass -eq 'computer'}).Name

} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
    Write-Warning -Message "Cannot find object with the identity $ADGroup."

} catch {
    # Unknown error exception.
    $ErrorParams = @{
        FunctionName = $MyInvocation.MyCommand.Name
        ErrorMessage = $Error[0].ToString()
        ErrorType = $Error[0].Exception.GetType().FullName
    }
    Send-ErrorMessage @ErrorParams
}

At first, I decided that the Send-ErrorMessage function wasn’t to be exported from its module. This meant that only the other functions in that module, would be able to use Send-ErrorMessage. This was done to prevent a human from directly interacting with the Send-ErrorMessage function. In time, I had a decision to make, as I decided I wanted to invoke Send-ErrorMessage from functions that weren’t in the same module, where Send-ErrorMessage was declared.

To do this, I had to export Send-ErrorMessage, thus making it available to all my functions regardless of module, and anyone that invoked the function manually. It was this second part I wanted to prevent; I didn’t want a user to be able to use this function. I only wanted specific functions, in varying modules, to be allowed to use the Send-ErrorMessage function.

What I needed was a way to prevent the function from doing much, if it was invoked by something other than an approved function. To do this required me to know what function invoked the Send-ErrorMessage function while the Send-ErrorMessage function was currently being invoked. I hadn’t considered this before, and so it took some thinking and testing. In the end, I have a solution.

To help demonstrate, let’s start with two functions: The first one is allowed to invoke Send-ErrorMessage, and the second one is not allowed to invoke Send-ErrorMessage. You’ll see this in the upcoming Send-ErrorMessage function. Before we get to that function, though, get comfortable with the two below functions.

Function Test-Allowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage
    }
} # End Function: Test-Allowed.


Function Test-NotAllowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage
    }
} # End Function: Test-NotAllowed.

They work this way: try to divide by 0 and intentionally error. The catch block will then invoke Send-ErrorMessage. Inside Send-ErrorMessage is where the determination is made as to whether or not the function that invoked it, is allowed to continue to use it. If the function that invoked it, is in the $AcceptableFunctions variable, then the code to send an email with the error information, which isn’t actually included in the below function, can be sent. If the function isn’t in the $AcceptableFunctions variable, then the user will get a warning and nothing will be sent.

Function Send-ErrorMessage {
    [CmdletBinding()]
    Param (
    )

    Begin {
        # This variable can be set a number of ways to include Get-Command.
        $AcceptableFunctions = 'Get-AJob','Test-Allowed','Get-Real'
        $CallingFunction = (Get-PSCallStack)[1].Command
        
        $Proceed = $false
        If ($CallingFunction -in $AcceptableFunctions) {
            $Proceed = $true
        }
    } # End Begin.

    Process {
        If ($Proceed) {
            Write-Verbose -Message "The calling function ($CallingFunction) is approved." -Verbose
            Write-Verbose -Message 'Beginning to send error message ...' -Verbose
            # DO STUFF!!!
        } Else {
            Write-Warning -Message "The calling function ($CallingFunction) is NOT approved."
            Write-Warning -Message "This function ($($MyInvocation.MyCommand.Name)) can only be invoked by approved functions."
        }
    } # End Process.
    
    End {
    } # End End.
} # End Function: Send-ErrorMessage.

Let’s invoke Send-ErrorMessage now, by invoking both the Test-Allowed and Test-NotAllowed functions. In the below example, the first two lines, that start with VERBOSE, are returned from invoking the Test-Allowed function, and the last two lines, that start with WARNING, are returned from invoking the Test-NotAllowed function. It works just like it should, allowing me to do something (send that error email), when I trust the calling function.

# Invoke Function.
Test-Allowed

# Invoke Function.
Test-NotAllowed

VERBOSE: The calling function (Test-Allowed) is approved.
VERBOSE: Beginning to send error message ...
WARNING: The calling function (Test-NotAllowed) is NOT approved.
WARNING: This function (Send-ErrorMessage) can only be invoked by approved functions.

Here’s the entire code, so you can copy it to the ISE, etc. and test it for yourself.

Function Send-ErrorMessage {
    [CmdletBinding()]
    Param (
    )

    Begin {
        # This variable can be set a number of ways to include Get-Command.
        $AcceptableFunctions = 'Get-AJob','Test-Allowed','Get-Real'
        $CallingFunction = (Get-PSCallStack)[1].Command
        
        $Proceed = $false
        If ($CallingFunction -in $AcceptableFunctions) {
            $Proceed = $true
        }
    } # End Begin.

    Process {
        If ($Proceed) {
            Write-Verbose -Message "The calling function ($CallingFunction) is approved." -Verbose
            Write-Verbose -Message 'Beginning to send error message ...' -Verbose
            # DO STUFF!!!
        } Else {
            Write-Warning -Message "The calling function ($CallingFunction) is NOT approved."
            Write-Warning -Message "This function ($($MyInvocation.MyCommand.Name)) can only be invoked by approved functions."
        }
    } # End Process.
    
    End {
    } # End End.
} # End Function: Send-ErrorMessage.


Function Test-Allowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage
    }
} # End Function: Test-Allowed.


Function Test-NotAllowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage
    }
} # End Function: Test-NotAllowed.


# Invoke Function.
Test-Allowed

# Invoke Function.
Test-NotAllowed

My first thought after these functions were written and tested, was what happens now that Send-ErrorMessage is in memory. Can I, as a user, just invoke Send-ErrorMessage myself? I can try, but I won’t get anywhere, as is demonstrated below.

Send-ErrorMessage
WARNING: The calling function (<ScriptBlock>) is NOT approved.
WARNING: This function (Send-ErrorMessage) can only be invoked by approved functions.

Keep in mind other ways to assign the $AcceptedFunctions variable. This doesn’t, and realistically, shouldn’t need to be a static. Instead, use the Get-Command cmdlet to collect the functions that will be allowed to invoke Send-ErrorMessage. If you use a prefix in your function names, it’s easy to isolate which functions can invoke Send-ErrorMessage. The below example assumes you prefix the nouns in your functions with Work, such as Get-WorkUser, and Get-WorkJob.

$AcceptableFunctions = (Get-Command -Noun Work*).Name

In closing, if you didn’t pick it up yourself, this works in thanks to the Get-PSCallStack function. It stores the called commands in a reverse array. That means, that the final function, Send-ErrorMessage, in this example, is index 0: (Get-PSCallStack)[0]. Therefore, the calling function, or command run in the console, is going to be (Get-PSCallStack)[1]. Tada! And to think, just a day or two ago, and I don’t think I had ever used the Get-PSCallStack cmdlet.

Update: There’s a Part II: Read it here.

PSMonday #7: Monday, June 13, 2016

Topic: Test-NetConnection

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Let’s spend a small part of this Monday morning learning about the Test-NetConnection cmdlet — a potential, telnet replacement. This cmdlet was introduced in PowerShell 4.0 which shipped “in box” with Windows 8.1 and Server 2012 R2.

Test-NetConnection returns a connectivity test to a remote computer. When the cmdlet is entered without a specific computer in which to test against, it will use internetbeacon.msedge.net, a domain owned by the Microsoft Corporation.

Test-NetConnection

ComputerName           : internetbeacon.msedge.net
RemoteAddress          : 13.107.4.52
InterfaceAlias         : Ethernet
SourceAddress          : 10.10.10.50
PingSucceeded          : True
PingReplyDetails (RTT) : 38 ms

This can be helpful, and provides proof of Internet connectivity, but often you’ll want to test a connection closer to the computer that initiated the test. In the case where you want to check a computer in a data center, or only a room away, you can use the -ComputerName parameter and provide a computer name as the parameter value, such as DC01 in this example.

Test-NetConnection -ComputerName DC01

ComputerName           : DC01
RemoteAddress          : 10.10.20.2
InterfaceAlias         : Ethernet
SourceAddress          : 10.10.10.50
PingSucceeded          : True
PingReplyDetails (RTT) : 39 ms

There’s a port parameter that can be used, that will allow you to test against a specific port on the remote computer.

Test-NetConnection -ComputerName DC01 -Port 5985

ComputerName           : DC01
RemoteAddress          : 10.10.10.2
RemotePort             : 5985
InterfaceAlias         : Ethernet
SourceAddress          : 10.10.10.50
PingSucceeded          : True
PingReplyDetails (RTT) : 42 ms
TcpTestSucceeded       : True

If you don’t know the port number, you may be able to use the -CommonTCPPort parameter and supply one of the accepted parameter values. There’s directions at the bottom of today’s PSMonday on how to view the help for this cmdlet, that indicate the accepted values. In this next example, we use RDP as the parameter value for this -CommonTCPPort parameter.

Test-NetConnection -ComputerName DC01 -CommonTCPPort RDP

ComputerName           : DC01
RemoteAddress          : 10.10.10.2
RemotePort             : 3389
InterfaceAlias         : Ethernet
SourceAddress          : 10.10.10.50
PingSucceeded          : True
PingReplyDetails (RTT) : 45 ms
TcpTestSucceeded       : True

There’s also an -InformationLevel parameter that will allow you to specify that you want detailed information returned, or in the case of the example below, only want a Boolean (true or false), value returned.

Test-NetConnection -ComputerName DC01 -CommonTCPPort HTTP -InformationLevel Quiet

False

There are more features to Test-NetConnection, to include a -TraceRoute and a -Hops parameter. If you’re interested in learning more about those, or want to read the full help — much of which you already know, now — enter one of the next two commands.

Get-Help -Name Test-NetConnection -Full
Get-Help -Name Test-NetConnection -ShowWindow

Next time you need to telnet from one host to another, try Test-NetConnection instead of telnet, and see if it isn’t a little more helpful.

PSMonday #6: Monday, June 6, 2016

Topic: Splatting and Hash Tables Continued

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

So, back to splatting: If you ever have a parameter that doesn’t need an assigned value, where the value is equal to true if the parameter is included, and false if it’s not — a switch parameter — then use $true or $false as the parameter value in the hash table. You can see an example of this below. Notice that in the example one of the parameter values wasn’t hardcoded, and instead was assigned using the value from another variable. Doing this isn’t always necessary, but it’s being shown, as an example for a time when it may be needed.

$Command = 'Get-ADUser'
$Params = @{
    Name = $Command
    ShowWindow = $true
}

Get-Help @Params

It should be said, that if you don’t include a switch parameter in the hash table, that it won’t be included when the cmdlet is run, and will therefore be false by default.

Let’s splat Send-MailMessage. This command, and its need for so many parameters and parameter values, makes it a great candidate for splatting, unlike the above Get-Help command where there was only two included parameters.

$MailParams = @{
    From = 'noreply@mydomain.domain.com'
    To = 'tommymaynard@mydomain.domain.com'
    Subject = 'Using Send-MailMessage and Splatting'
    Body = "This email sent from $env:USERNAME."
    SmtpServer = 'smtp.mydomain.domain.edu'
}

Send-MailMessage @MailParams

Bonus: If you have more parameters to add to a command, and they’re not included in the hash table (for whatever reason), you can still add them to the command.

Send-MailMessage @MailParams -Attachments 'C:\file.txt'

Second Bonus: What could be done instead of adding a separate parameter and parameter value to the command, as we saw in the above Bonus section, is adding another key-value pair to our hash table using the .Add() method.

$MailParams.Add('Attachments','C:\File.txt')

$MailParams

Name              Value
----              -----
Attachments       C:\File.txt
SmtpServer        smtp.mydomain.domain.com
Subject           Using Send-MailMessage and Splatting
Body              This email sent from tommymaynard.
From              noreply@mydomain.domain.com
To                tommymaynard@mydomain.domain.com

Send-MailMessage @MailParams

That’s it. Enjoy your Monday.

PSMonday #5: Monday, May 30, 2016

Topic: Splatting and Hash Tables

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

There are a couple things in Windows PowerShell that have a strange name. One of them, is splatting.

Consider one of those cmdlets that requires several parameters and parameter values. The below command is an older, modified command that was used to create a PowerShell Web Access authorization rule. This command, is likely going to take up some space and either wrap in the console, or add the need to scroll horizontally in the ISE.

# This example is a lengthy, single command.

Add-PswaAuthorizationRule -RuleName SharePoint-AppAdmins-Rule -ComputerName 'SPCA01' -UserGroupName MYDOMAIN\SP-AppAdmins -ConfigurationName Tools.SharePoint

The length of this command makes it difficult to read and comprehend. Some might be inclined to use the backtick (`) as a line continuation character to help make it more readable.

# This example uses backticks as line continuation characters.

Add-PswaAuthorizationRule `
-RuleName SharePoint-AppAdmins-Rule `
-ComputerName 'SPCA01' `
-UserGroupName MYDOMAIN\SP-AppAdmins `
-ConfigurationName Tools.SharePoint

This option helps some, but I’d recommend not using the backtick as a line continuation character in about 99% of instances. It’s tiny, it’s difficult to see, and if there’s a space after it, the command will throw an error.

Back to splatting. When you splat your parameters and parameter values, you first create a hash table, as signified by @{}. For those that may not know, a hash table contains key-value pairs. The first key in the below example, is RuleName. The associated, first value is ‘SharePoint-AppAdmins-Rule’. Hash tables are also called dictionary objects, or more often, associative arrays. Here’s an example of creating and storing a hash table in a variable with the same parameters and parameter values used in the above examples.

$Parameters = @{
    RuleName = 'SharePoint-AppAdmins-Rule'
    ComputerName = 'SPCA01'
    UserGroupName = 'MYDOMAIN\SP-AppAdmins'
    ConfigurationName = 'Tools.SharePoint'
}

Now, that’s easy on the eyes. Enter the variable name to see the key-value pairs stored in the hash table.

$Parameters

Name                           Value
----                           -----
RuleName                       SharePoint-AppAdmins-Rule
ComputerName                   SPCA01
UserGroupName                  MYDOMAIN\SP-AppAdmins
ConfigurationName              Tools.SharePoint

Now when you run the command, you enter the cmdlet name, the @ symbol, and the name used for the variable (without the dollar sign), as seen in the below example.

Add-PswaAuthorizationRule @Parameters

Now, when this command is run, it will include all the parameters and parameter values defined in our hash table. That’s it for this week. We’ll pick up next week and talk a little more about splatting.

Linux Prompt on Windows – Part II

A while ago, I wrote myself a new prompt function and shared it here. Well, I’m here to report that I’m still using it in both the ConsoleHost and the ISE. Here’s an image from the first post that shows what it looks like.

duplicate-the-linux-prompt01

The thing about this prompt function, is that it also modified the WindowTitle to reflect the exact same information — the user, the computer, the location on the current drive, and whether or not you’re admin (# = admin vs. ~ = not admin). I love that too, but there’s turned out to be one little inconvenience. Besides the minutely different icons, I can’t quickly tell if I’m about to open the ISE or ConsoleHost from its taskbar icon. Therefore, I modified the prompt function every so slightly to indicate which host is which. Take a look.

an-addition-to-my-linux-powershell-prompt01

Now I can better determine which host I want — CH for ConsoleHost and ISE for, well, the ISE — before I bring it back to the front. As expected, it only took a minor change. I added a switch statement that evaluated the value in $Host.Name. Based on its value, it populated a variable called $HostName that’s used as a part of the assigned valued in the WindowTitle.

Function Prompt {
	(Get-PSProvider -PSProvider FileSystem).Home = $env:USERPROFILE

	# Determine if Admin and set Symbol variable.
	If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
		$Symbol = '#'
	} Else {
		$Symbol = '$'
	}
	 
	# Write Path to Location Variable as /.../...
	If ($PWD.Path -eq $env:USERPROFILE) {
		$Location = '~'
	} ElseIf ($PWD.Path -like "*$env:USERPROFILE*") {
		$Location = $PWD.Path -replace ($env:USERPROFILE -replace '\\','\\'),'~' -replace '\\','/'
	} Else {
		$Location = "$(($PWD.Path -replace '\\','/' -split ':')[-1])"
	}

	# Determine Host for WindowTitle.
	Switch ($Host.Name) {
		'ConsoleHost' {$HostName = 'CH '; break}
		'Windows PowerShell ISE Host' {$HostName = 'ISE'; break}
		default {}
	}

	# Create and write Prompt; Write WindowTitle.
	$Prompt = "$($env:USERNAME.ToLower())@$($env:COMPUTERNAME.ToLower()) $Location $Symbol "
	$Host.UI.RawUI.WindowTitle = "$HostName`: $Prompt"
	$Prompt
}

Update: After a week or so, I updated the function again. I couldn’t stand the capital CH and ISE. So, that’s fixed: It’s ch and ise, now. There may come another change, too. When I’m not on the C:\ drive, I’d like to include an indication in the WindowTitle. Here’s the update.

PSMonday #4: Monday, May 23, 2016

Topic: Add and Concatenate Numbers and Strings

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

In last week’s PSMonday we determined that numbers can be strings, but that when they are, they lose their numeric properties. As strings, they don’t and won’t act like numbers anymore. For instance, if you “add” the string ‘5’ plus the string ‘5’ you’re not going to get 10. As the plus sign is also a concatenation operator, you end up with the string value of ’55’.

'5' + '5'

55

If you were working with numeric values, such as 5 + 5 — notice there’s no quotes around those digits — then you would get the numeric result of 10.

5 + 5

10

Now that we know what happens when we concatenate two strings and add two numbers, you might wonder what happens when we do this with a string, and a number.

Here’s the key: The object on the left decides what happens. In the first below example, the numeric value on the left coerces the string value on the right into becoming a number, and then the numbers are added. In the second example, we see the opposite. The string value on the left coerces the number on the right into being a string, and then the strings are concatenated.

5 + '5'

10

'5' + 5

55

Expect this to fail at times. In the next example, the number on the left cannot coerce the string value “string” into becoming a numeric value — it’s not possible — and therefore it fails.

5 + 'string'

Cannot convert value "string" to type "System.Int32". Error: "Input string was not in a correct format."

Had this been written in reverse, as it is below, it would work and the result would be ‘string5’. This is because the numeric value 5 can become the string value ‘5’, as we saw earlier.

'string' + 5

string5

Short and sweet today; thanks for reading this PSMonday.

PSMonday #3: Monday, May 16, 2016

Topic: Quotes and Strings, and Their Rules

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Today, I’d like to share how to handle quotes in a string. A string is any group of alphanumeric characters to include any punctuation. Think of a string as a sentence, or a word. A string, at its smallest component, can be a single letter, a number, or as mentioned, punctuation. Yes, numbers can be strings too, although they lose their numeric properties.

It is recommended to always use single quotes to encapsulate a string, unless there’s a need for double quotes (they have a special purpose). This is considered best practice by the PowerShell community. The first example string has no internal, interruptive punctuation, such as another single quote, or apostrophe.

$String = 'A string without any quotes.'
$String

A string without any quotes.

The next example string uses internal, double quotes. They are non-interruptive in this instance, because the string begins and ends with single quotes.

$String = 'A string with "double" quotes.'
$String

A string with "double" quotes.

This next example includes both single and double quotes inside a string, that begins and ends with single quotes. Notice that to do this, we needed to put two side-by-side single quotes, around the word single.

$String = 'A string with ''single'' and "double" quotes.'
$String

A string with 'single' and "double" quotes.

One of the things that you might expect to work, is escaping the single quotes instead of using side-by-side single quotes. This doesn’t work.

$String = 'This is a string with `'single`' and "double" quotes.'

At line:1 char:36
+ $String = 'This is a string with `'single`' and "double" quotes.'
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~~~~~~~~
Unexpected token 'single`'' in expression or statement.

Although I said to use single quotes to encapsulate a string, you can use double quotes if you have internal single quotes, or apostrophes, and you don’t want to use side-by-side single quotes.

$String = "I'm only using 'single' quotes in this string."
$String

I'm only using 'single' quotes in this string.

The final reason you might use double quotes is when you want to expand, or display, the value stored in a variable(s) inside your string. You can’t do this with single quotes. Notice in the below example that we’re using side-by-side double quotes in this string.

$Word1 = 'string'
$Word2 = 'quotes'
$String = "This is a $Word1 with 'single' and ""double"" $Word2."
$String

This is a string with 'single' and "double" quotes.

Bonus: Back to escaping, you can escape double quotes, so the example above could have been written like the example below, and therefore, not required using side-by-side double quotes. Don’t forget about apostrophes in your contractions (I’m, don’t, aren’t, etc.). You’ll need to determine a way around those as you create strings where they’re included.

$String = "This is a $Word1 with 'single' and `"double`" $Word2."
$String

This is a string with 'single' and "double" quotes.

And with that, the third PowerShell Monday is complete. As mentioned previously, please let me know if there’s something you’d like to see discussed.

PSMonday #2: Monday, May 9, 2016

Topic: Best Way to Filter the Results of Your Commands

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Today we’re going to discuss the best way to filter the results of our commands. These first two commands return the same results — the services on the local computer that begin with the letter L. Let’s discuss why one is better than the other, right after we take a look at the commands.

Get-Service | Where-Object Name -like L*

# And

Get-Service -Name L*

While piping the results of one command to another is beneficial in Windows PowerShell, it’s not always the best idea. If a cmdlet has a way to filter your results without the need to pipe it to a filtering cmdlet, such as Where-Object, then use it. The reason this is faster is because in the first above example we return ALL the services first and then filter them one by one. In the second example, we filter immediately, which means we never stop to consider services that start with any other letter than L. You might someday hear this called the filter left rule. The idea is to do as much filtering as close to the left of your commands as possible. You might also here it as filter left, format right. This is to say that formatting cmdlets (Format-Table, Format-List, etc.) should be as far to the right of a command(s) as possible (typically the last cmdlet in a command that includes piping).

Here’s an example from Active Directory (AD). In the first command we return EVERY AD user object and then filter them, whereas in the second command we filter immediately and only process the users we want to return. In the case of AD, it’s not only faster, but it’s going to be less resource intensive than peering at every AD user object in the entire domain, to only return what might be a handful of users.

Get-ADUser -Filter * | Where-Object Surname -eq 'Smith'

# And

Get-ADUser -Filter {Surname -eq 'Smith'}

I ran the two above commands through the Measure-Command cmdlet in order to measure their execution times. I did this around 10 p.m. Saturday evening using my last name. The first command, that checked every AD user object, took 6 1/2 minutes to complete. The second command took an average of 30 milliseconds. That’s a big difference.

Bonus: Here’s how to determine which cmdlets have a specific parameter. In this case, we’re checking for cmdlets that include a -Filter parameter. The below image only shows the first nine cmdlets of the sixty found on my computer that include this parameter. Remember, however, that in the case of the Get-Service cmdlet, that we didn’t filter with a -Filter parameter. In that instance, we used the -Name parameter, as it accepted wildcards. Use Get-Help to determine if a cmdlet’s parameters accept wildcards, or not.

Get-Command -ParameterName CommandType

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Add-Content                                        Microsoft.PowerShell.Management
Cmdlet          Clear-Content                                      Microsoft.PowerShell.Management
Cmdlet          Clear-Item                                         Microsoft.PowerShell.Management
Cmdlet          Clear-ItemProperty                                 Microsoft.PowerShell.Management
Cmdlet          Copy-Item                                          Microsoft.PowerShell.Management
Cmdlet          Copy-ItemProperty                                  Microsoft.PowerShell.Management
Cmdlet          Get-ADAuthenticationPolicy                         ActiveDirectory
Cmdlet          Get-ADAuthenticationPolicySilo                     ActiveDirectory
Cmdlet          Get-ADCentralAccessPolicy                          ActiveDirectory

That’s all. We’ll be back at it next Monday.