Splatting Parameters Pt 2 – Remote Possibilities

In my last post I talked about using splatted parameters to shorten long command lines and help organize code.

Splatting parameters is usually discussed as a way to pass parameters to cmdlets, but it can also be used to pass parameters to scripts, functions, filters and even an anonymous script block.

Here’s a simple script block that just spits out whatever arguments it’s given:

{$args}

Let’s invoke that, and splat one of the hash tables from the vacation notice script:

Param (
$ImportFile     =  './user_vacation_list.csv',
$EmailFrom      =  'HR@mycompany.com',
$EmailSubject   =  "'Unused vacation reminder'",
$SMTPServer     =  'mail.mycompany.com'
)
 
$AllEmailParams =
        @{
          From       = $EmailFrom
          Subject    = $EmailSubject
          SMTPServer = $SMTPServer
          }

&{$args} @AllEmailParams

-Subject:
'Unused vacation reminder'
-From:
HR@mycompany.com
-SMTPServer:
mail.mycompany.com

The splat operation took the hash table keys and added a hyphen to the front and a colon to the end to make them conform with Powershell parameter syntax, and then spit out each key and it’s value.

That will get implicitly cast as [string] when it’s gets used as a parameter set. Let’s do that explicitly to see what it will look like:

"$(&{$args} @AllEmailParams)"

-Subject: 'Unused vacation reminder' -From: HR@mycompany.com -SMTPServer: mail.mycompany.com

And that’s how splatting works.

Now, back on the vacation notification front, it turns out my script has stopped working because the SMTP server I was using is no longer available. I do have an Exchange server, but by default Exchange receive connectors won’t do anonymous mail relay without special configuration of the access controls. But here a little secret about those receive connectors – the access controls only apply to network connections. An Exchange transport server will relay mail for a script running locally using ‘localhost’ as it’s relay host, even if it’s receive connectors aren’t configured for relay.

So I can use that Exchange server for my mail relay if I change my -SMTPServer to ‘localhost’, and run my script there.
I have remoting enabled on that server, so I should be able to do that using Invoke-Command to run the Send-MailMessage command there. But now all those parameters that are using local variables as arguments are a problem. I can’t put the hash tables in he script block because the variables in them are on my computer, and Invoke-Command won’t let me splat them to it’s Scriptblock. It will only take an argument list, and you can’t used named parameters there. I’ll have hope all those parameters are positional, figure out what the position is for each one, and construct and argumentlist in that order. If I can’t do that, I’ll have to rewrite it to use explicit $args elements, or re-write it by explicitly scoping all the variables with the $Using: prefix. But there is another way.

Sometimes it’s easier to get a script block using local variables by creating it from an expandable string. Using what we know about splatting, we can take our existing hash tables of arguments, and modify the existing script to run that Send-MailMessage command on the Exchange server like this:

Param (
$ImportFile     =  './user_vacation_list.csv',
$EmailFrom      =  'HR@mycompany.com',
$EmailSubject   =  'Unused vacation reminder',
$SMTPServer     =  'localhost' #Changed to 'localhost'
)
 
$AllEmailParams =
        @{
          From       = $EmailFrom
          Subject    = $EmailSubject
          SMTPServer = $SMTPServer
          }
 
Foreach ($User in import-csv $ImportFile)
 {
   $ADUser = Get-ADUser $User.Name -Propertes Mail,Manager
   $Manager = Get-ADUser $User.Manager -Properties Mail
 
   $ThisEmailParams = 
        @{
          To   = $ADUser.Mail
          Cc   = $Manager.Mail
          Body = "You have $($User.VacationDays) unused vacation days."
         }
 
#These lines added in place of the Send-MailMessage command

  $Scriptblock = [Scriptblock]::Create(
   "Send-MailMessage $(&{$args} @AllEmailParams) @ThisEmailParams)")
 Invoke-Command -ScriptBlock $Scriptblock -ComputerName ExchangeServer
 }

Create the script block using the Send-MaillMessage command and the splat strings, and it’s ready to go. All the local variables are already expanded to literal values in the script block, and it’s ready to be invoked on the remote system.
Since I’ve only made one parameter change and added a couple of extra lines to make this work with the existing code, it would be relatively easy to add a another parameter set to include the Exchange server and have it work either way.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s