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.

More AWS PowerShell Changes Due to Twitter (and Me)

It happened again. My Tweet, linked to a post here, became the catalyst for a change in the AWS PowerShell module. I’m 2 for 2. This time, AWS didn’t simply correct the spelling of a parameter name; they wrote a new cmdlet. Remove-EC2Instance works as advertised. It’ll prompt for confirmation before terminating an EC2 instance unless the Force parameter is also included with the command. It is flawless. You can always get the newest AWSPowerShell module at http://aws.amazon.com/powershell.

What this didn’t do, however, is fix Stop-EC2Instance. Steve, the individual that spotted this and my last AWS Tweet, and likely made these corrections and additions, said they couldn’t remove the malfunctioning Terminate parameter from Stop-EC2Intance. For those that didn’t read my last AWS post, the Terminate parameter never prompted for confirmation before terminating an instance (unless a change has been made since Remove-EC2Instance was released).

I read an interesting article recently that indicated to use termination protection:

Use termination protection for non-auto-scaling instances. Thank me later.
If you have any instances which are one-off things that aren’t under auto-scaling, then you should probably enable termination protection, to stop anyone from accidentally deleting the instance. I’ve had it happen, it sucks, learn from my mistake!”

While it was still quite amazing to get a second change made to the AWSPowerShell module, I’d recommend that everyone continue to be careful with Stop-EC2Instance. Perhaps you can write your own wrapper function, with the same name as the cmdlet (and therefore, it’ll be the default when Stop-EC2Instance is called), that forces confirmation. Then again, you can just use Remove-EC2Instance now. We’ll hope that when people look to terminate an EC2 instance, they find Remove-EC2Instance before they find the Terminate parameter in Stop-EC2Instance, such as I would have, had it previously existed. 🙂

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.

Two Year Anniversary

Just a quick post to mention that tommymaynard.com has been up for two years this month. That’s June 2014 through 2016.

I remember early 2014 and thinking about buying the domain name, about the prospect of writing assistive PowerShell content, and about working to associate my name with Windows PowerShell. Two years later, and it’s been somewhat of a success — I didn’t know if I could do it, but I have. It’s almost difficult to believe, but I’ve been told that my efforts have helped people improve their knowledge of PowerShell, and even assisted in their careers. To help, was always the original idea. To learn it deeper, myself, was a welcomed benefit.

There’s no stopping here. This post is a just a brief intermission until the next topic.

Update: As of yesterday (June 7, 2016), I’ve renewed the tommymaynard.com domain for another two years. 🙂

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.

Windows PowerShell Constrained Endpoints, Proxy Functions, and Just Enough Administration

I had a recent request for some information in regard to constrained endpoints. There was a lengthy article posted in a PowerShell.org TechLetter earlier this year that I authored (it actually won me a free 4-day pass to the PowerShell + DevOps Global Summit 2016). As of now, those don’t appear to be back online, since the recent redesign and changes over there.

Therefore, I’ve opted to post the paper here. Keep in mind, as you read this, that we should be moving away from constrained endpoints of the past and toward JEA endpoints of the future. Realistically though, I’m pretty sure those constrained endpoint cmdlets of the past, are the ones of the future, with some new additions. I should probably go get my hands dirty. Enjoy!

Note: Throughout the course of this previously written paper, I used the term proxy function rather loosely. I’ve done some thinking since then, and no longer use it that way. Going forward, or if I were to rewrite this paper, I wouldn’t use the term proxy function. Instead, I would refer to the included functions as wrapper functions, as they wrap other commands, and didn’t have anything to do with obtaining the cmdlet metadata and using that to write a function. If they had, then they’d be proxy functions.

Windows PowerShell Constrained Endpoints, Proxy Functions, and Just Enough Administration (3807 downloads )

Creating Multiple Credential Objects Part II

Download the updated function here: https://gist.github.com/tommymaynard/98031ccd5de67005bf3063db06a33851

Back in January 2015, I wrote a post and uploaded an advanced function to the TechNet Gallery that allows a user to create multiple credential objects. Edit: The TechNet Gallery no longer hosts scripts/modules, so use the GitHub Gist at the top and bottom of this page. The multiple part is up to the user, such as they might enter a command as in the below example. Due to the -Set 2 parameter and parameter value, they would get prompted twice, to enter a username and password combination. When the function was completed, they would have two credential objects, with the first stored in $CredSet1 and the second stored in $CredSet2.

New-TMMultiCred -Set 2

I had always wanted to make a couple of changes to the advanced function, and so I have. Now the function includes a -NoGui parameter, that will not require the username and password be entered into the Get-Credential GUI, and can instead be entered directly into the console. I should mention that if this was run in the ISE, the password would actually invoke a small GUI for the password, as is standard when using Read-Host‘s -AsSecureString parameter in that host.

The other addition I wanted to add is that the advanced function produces objects, instead of using the Write-Output cmdlet to display information. Now, instead of the function writing this:

SUCCESS: Credential Set 1 stored in $CredSet1
SUCCESS: Credential Set 2 stored in $CredSet2

the function writes this:

CredSet             Variable            UserName                                 Password
-------             --------            --------                                 --------
1                   $Credset1           mydomain\admin       System.Security.SecureString
2                   $Credset2           user1                System.Security.SecureString

Neat, right? So download it and try it out. I think it was a decent addition to an already helpful advanced function.

Download the updated function here: https://gist.github.com/tommymaynard/98031ccd5de67005bf3063db06a33851

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.