Scripting Games 2013: Event 4 Notes

It is all downhill from here folks! Event 4 is in the books and we only have 2 more to go! Everyone has been doing an outstanding job with their submissions and it is becoming clear that people are learning new things and showing some great techniques with their code.

Of course, this doesn’t mean that there isn’t room for improvement with some submissions to make them even better or just some simple mistakes that can be cleaned up to make average submissions into amazing submissions. With that, its time to dive into my notes…

Send-MailMessage, not Net.Mail.MailMessage

I’ve seen several submissions where people have an optional email message that can be sent out. I had no issues with this and in fact applaud those that did! There is nothing wrong with added functionality to a script to make it better. What I did have issues with was those that chose the hard road of building out the email system using .NET instead of the readily available Send-Mailmessage cmdlet.

Compare these examples:

.NET Way

#SMTP server name
$smtpServer = $smtpServer
 
#Creating a Mail object
$msg = new-object Net.Mail.MailMessage
 
#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
 
#Email structure
$msg.From = $EmailFrom
$msg.ReplyTo = $EmailFrom
$msg.To.Add($EmailTo)
$msg.subject = $EmailSubject
$msg.attachments.add($Attachment)
$msg.body = $EmailBody
 
#Sending email
$smtp.Send($msg)

PowerShell Cmdlet

$email = @{
    To = $EmailTo
    From = $EmailFrom
    Subject = $EmailSubject
    Body = $EmailBody
    SMTPServer = $smtpServer
}
Send-MailMessage @Email

The proof is not only in the lines of code, but also in the readability of the code.

Again, don’t use .NET types unless absolutely necessary as it creates more work for you and really goes against the best practice of using PowerShell cmdlets in PowerShell scripts. Please try to force yourself away from this habit.

A better [ValidatePattern()] with [ValidateScript()]

I didn’t count this against anyone, but I have seen a lot of people using [ValidatePattern()] to do some regular expression (RegEx) matching. While it does work in preventing incorrect data from being supplied to a parameter, the error message is something that is just terrible for a user to try and interpret. Take this example:

Function Test-Something {
    [cmdletbinding()]
    Param(
        [parameter()]
        [ValidatePattern("^(\d{1,3}\.){3}\d{1,3}$")]
        [string]$IPAddress
    )
    $IPAddress
}

If a wrong value is given…

image

The false IP fails the test, but unless you know RegEx, the error message pretty much means nothing at all. Enter [ValidateScript()] to make this better. The one thing to watch out for with this is that you provide more to the scriptblock than this:

Function Test-Something {
    [cmdletbinding()]
    Param(
        [parameter()]
        [ValidateScript({($_ -match "^(\d{1,3}\.){3}\d{1,3}$")})]
        [string]$IPAddress
    )
    $IPAddress
}

image

While the idea is there, it still doesn’t help the user to know what is wrong. Instead, add and If/Else block to handle the matching and use Throw to put a more descriptive error message.

Function Test-Something {
    [cmdletbinding()]
    Param(
        [parameter()]
        [ValidateScript({
            If ($_ -match "^(\d{1,3}\.){3}\d{1,3}$") {
                $True
            } Else {
                Throw "$_ is not a valid IPV4 address!"
            }
        })]
        [string]$IPAddress
    )
    $IPAddress
}

image

Now the error message has more meaning to it!

User the –Properties Parameter for Get-ADUser

As with Get-WMObject, you need to use the parameters available in the cmdlets to have the most efficient submission. If  you specify –Properties * and then try to pull data, you are bringing back more data than needed. Instead use –Properties and specify each property that you want.

LastLogon or LastLogonTimeStamp property?

This one would take some research to determine the best course of action. While either one will get you the last logon date required for the submission. You have to consider which is the best approach. You can use the LastLogonTimeTimestamp but under the knowledge that it is only replicated every 9-14 days. The other option is that you can use LastLogon instead which is not replicated to all Domain Controllers meaning that in order to accurately determine the accurate timestamp of the user account, you must query each of the domain controllers in the environment and take the highest value found. Some people did this, others took a different route. Here are my points on each one:

  1. LastLogonTimeStamp
    1. 6-14 days to replicate
    2. May not be the most accurate
    3. Best performance as only one Domain controller needs to be queried
    4. Ok if accuracy isn’t the top priority and timeline is small to get this done
  2. LastLogon
    1. Does not replicate at all
    2. Most accurate
    3. Requires querying every domain controller and finding the largest value
      1. Performance hit
    4. Ok if accuracy is important and time isn’t an issue

So what does this mean? Well, it really depends on the scenario. If someone did go for the lastlogon attribute, I hope that they knew to hit all of the domain controllers, otherwise the report will be suspect as the data will not be that accurate.

Use of Here-Strings for HTML code

I’ve seen more people using Here-Strings instead of concatenating strings when building out their html /CSS styles for use with ConvertTo-HTML. And for that I say Good Job! I still see some people trying to concatenate strings and hope that those habits change for the last 2 events (if applicable). I did see somebody use a stringbuilder class to construct the html code. Very interesting and I can’t say that I really have much of an opinion on it as it is something different and the person does use it beyond the initial creation of the styles.

All in all, great job with this event! I am looking forward to seeing what everyone brings to the table with Event 5!

This entry was posted in 2013 Scripting Games Judges Notes, powershell, Scripting Games 2013 and tagged , , , , . Bookmark the permalink.

6 Responses to Scripting Games 2013: Event 4 Notes

  1. Pingback: PowerShell Parameter Validation: Building a Better ValidatePattern with ValidateScript | Mike F Robbins

  2. Tim says:

    Boe,

    I am guilty of using ValidatePattern with a regex and I just couldn’t think about how to make the error more user friendly. I will definitely be using the if/else option in the future. I am guessing in the same vein you could use try/catch/finally.

    Is there a reason why you opted to use if/else with a throw over a try/catch?

    Thanks for you tips.

    Tim

    • Boe Prox says:

      Excellent question, Tim!
      The reason for the If/Else is because the match returns a Boolean value that fits perfectly for this type of statement and Try/Catch wouldn’t work because no terminating errors are thrown. The Throw portion is where I can specify a more user friendly error message that will be shown if the input wasn’t quite what we are looking for.

      Hope this helps…
      Boe

      • timaitken390825094 says:

        Ahh yes, you can’t set ErrorAction on a match.

        Thanks again for your tips along the way (and all the other judges), I have certainly learnt a lot playing the games.

        Tim

  3. vernanderson says:

    Awesome tip Boe I still use telnet commands out of habit because it was drilled in after doing that thousands of times when i was an Exchange admin. I need to make the switch to PowerShell

  4. Pingback: Scripting Games 2013: Event 4 Notes | PowerShell.org

Leave a reply to timaitken390825094 Cancel reply