Guest Post On Hey, Scripting Guy! on Using The UpdateServices WSUS Module

My latest guest spot on Hey, Scripting Guy! is live today! I continue my article on managing the new WSUS server on Windows Server 2012 using the UpdateServices module. I go through each of the cmdlets that are available and demo how to use each one. Check it out and let me know what you think!

http://blogs.technet.com/b/heyscriptingguy/archive/2013/05/27/use-the-updateservices-module-to-manage-wsus.aspx

Posted in Modules, powershell, WSUS | Tagged , , , , | Leave a comment

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!

Posted in 2013 Scripting Games Judges Notes, powershell, Scripting Games 2013 | Tagged , , , , | 6 Comments

Scripting Games 2013: Event 3 Notes

Wow, it is hard to believe that we are now halfway through the Scripting Games! As the events have progressed, I have seen a lot of improvement with the techniques as well as seeing new techniques that continue to impress me. On the flip side, I have seen some mistakes or assumptions when coding that cause a potential 5 star script to be a 2 or 3 star script. The best part about all of this is that we are all (yes, even the judges) learning new things that can only help to improve everyone’s scripting knowledge.

With that, it is time to take a look at the things that could be improved upon or should be avoided altogether.

-Filter vs. Where-Object

This was probably the biggest issue I have seen with the submissions for Event 3. Too many submissions were taking the output of Get-WMIObject and piping the results into Where-Object to get all of the local hard drives. The sooner you get used to working with –Filter, the better off you will be working with scripts and one liners to query WMI on both local and remote systems. When you use the Filter parameter, especially on remote systems, the filtering is performed on the remote system and only the objects that you filtered for will be passed back to the local system.

Some people claim that it is an ugly approach and I disagree. If I am looking for only hard drives, which one looks worse:

Filter

drivetype=3

Where-Object

$_.drivetype –eq 3

To me, the obvious answer is using Filter and supplying the WQL statement. Sure the statements may get complicated, but so could using the Where-Object. And if you might not be sure as to what type of WQL statement to use, simply look it up. There are plenty of resources available that you can use to figure out the proper query.

I’ve also heard that performance was the reason for using Where-Object locally vs. –Filter and that it isn’t much better to consider using the –Filter parameter. Lets compare performance just to see.

Filter

(Measure-Command {
    Get-WmiObject -Class Win32_LogicalDisk -Filter 'drivetype=3'
}).TotalMilliseconds

image

 

Where-Object

(Measure-Command {
    Get-WmiObject -Class Win32_LogicalDisk | Where {
        $_.driveType -eq 3
    }
}).TotalMilliseconds

image

About a 50 millisecond difference. Pretty good difference to me. Some might disagree, but the fact is that using –Filter has better performance. So why is that the case? Let’s take a closer look at each command. If you know PowerShell, you know that Where-Object takes the input from whatever command is before it in the pipeline and starts performing a comparison against the object in the pipeline based on the data you provide.

Get-WmiObject -Class Win32_LogicalDisk | Where {
    Write-Verbose $_ -Verbose
    $_.driveType -eq 3
}
 

image

As you can see, each object is being looked at when just using Where-Object. I wonder what happens when I use the –Filter parameter…

Get-WmiObject -Class Win32_LogicalDisk -Filter 'drivetype=3' | Where {
    Write-Verbose $_ -Verbose
}

image

Instead of going through 4 items, I only see the 3 that are actual hard drives. This is the efficiency of using a parameter on ANY cmdlet vs. using Where-Object.

So bottom line: –Filter is not only a best practice for Get-WMIObject, but it is also more efficient and provides better performance.

Hard Coded Paths

Something that was driving me crazy during this event, especially on the beginner side was the use of hard coded paths to output the html file. When you use a path such as C:\smith\awesomefolder as the directory, you cannot expect it to work for me. I shouldn’t have to modify your code to make it work for me. It should work without any issues. The solution for this would be to use something like $pwd, or also in some scripts I saw (good job, by the way!), $Env:TEMP  or $Env:USERPROFILE. There are other possibilities, but the main thing is that you must make sure that it is universal to the everyone who uses it.

.Net Types

This is the PowerShell scripting games, not the .Net games. Don’t use [DateTime]::now, when there is a perfectly good cmdlet called Get-Date that will accomplish the same thing. I even saw this: [environment]::MachineName being used. Really? You can get the same thing out of $Env:Computername.

Another person took the time to create their own network checker using System.Net.NetworkInformation.Ping. The same thing can be done by using Test-Connection.

Unless there is compelling need to use this (haven’t seen it yet) to use a .Net type to accomplish your goal, stick with the cmdlets and you will be better off.

Over Commenting

This is pretty self explanatory. We don’t need a 2-3 line comment talking about how you are querying WMI on a local system and then continuing this trend for each and every line of code. Not every line of code requires a comment, let along multiple lines of commenting. Keep it simple and clean and everything will be Ok.

Some Final Points

  • Consider use of [pscustomobject] vs. Net-Object PSObject for V3 code
  • Stop using back ticks as line breaks.
  • If the event says to do a one line, then you should shoot for a one liner and avoid using semicolons because it is no longer a one liner.
  • No more concatenating (Ex: “this” + $something + “is great!”). We are not in the vbscripting games.
  • Also avoid using += to append lines to an existing variable. Use a Here-String instead.

Keep up the great submissions and I am looking forward to seeing what you do with Event 4!

Posted in powershell, Scripting Games 2013 | Tagged , , , | 4 Comments

Scripting Games 2013: Event 2 Notes

I spent some time last week and this weekend to compile a list of notes of what I have seen with the Event 2 submissions that should show improvement. I touched up on some items with my previous article where I picked out some submissions that I liked and didn’t quite like but wanted to touch on a few more things. Some of this feels like a repeat of last week and even last years games, but that is Ok. This is all about learning and as long as everyone takes what all of the judges have been writing about, then there will be nothing but great improvements during the course of the games.

When is a one-liner not a one-liner?

The answer is when someone uses a semicolon “;” and assumes that counts as a one-liner. Sorry folks, but this doesn’t work. A one-liner is something that is a complete continuation of a string of commands. In PowerShell, a semicolon is a line break which means that the one-liner that was being put together has now became a multi-one-liner.

Here is what not to do:

$OS = Get-WMIObject -Class Win32_OperatingSystem ;$ComputerSystem = Get-WmiObject -Class Win32_ComputerSystem; New-Object PSObject -Property @{Computername=$ComputerSystem.Name;OS=$Os.Caption}

It seems like a one-liner, but with the semicolons, it is nothing more than a one-liner lie. In fact, the only time here that the semicolon is legal is with the New-Object cmdlet that takes a hash table as input.

A one liner is not required for the Scripting Games in the beginner event. It is a nice to have, but it is also a risk because if you try to force it as such, it could come back to bite you when it comes to voting. My advice, split the code up on lines because not only will it prevent possible issues in the code, it is going to look tons better!

Formatting your code

Moving from one-liners to proper code formatting so it can easily be read by not only the voting community, but also applies to production scripts that may have to be read 6 months from by someone else or even the future you. Think to yourself, “Will I know exactly what is going on here in 6 months?”. If you cannot answer this question or answer No, then stop, go back and take the time to re-format the code so it is more readable.

Here are some bullet points to think about when writing code:

  • Space out your code; separate by code blocks so it is not just one line after another of code
  • Use the proper line continuation characters: commas“,”, opening square bracket “{“ and pipe symbol “|”; please for the love of everything good, do not use a backtick “`” as a line continuation. This is also a lie and must be stopped!
  • Indent your code where needed. Examples would be when you use a natural line break (first bullet point) or during a ForEach/Where block and If/Else block. Doing so will make it easier to read and organize your code.

Splatting instead of back ticks for parameters

Splatting is not only great for working with various cmdlets and making input a lot easier when in a loop, but it is also a smart idea to make your code easier to read and avoiding the use of back ticks when working with multiple parameters.

Send-MailMessage -To user@domain.com `
                 -From user1@domain.com `
                 -Subject Test `
                 -SMTPServer server@domain.com `
                 -Body "This is a test"

This is not a good way of handling the parameters in a script. Sure, it is better than having them on one line and navigating that mess, but this still doesn’t fit a good practice. This is where splatting comes into play.

$email = @{
    To = 'user@domain.com'
    From = 'user1@domain.com'
    Subject = 'Test'
    SMTPServer = 'server.domain.com'
}
...
#Some code
...
#Add the output into the body of the email
$email.Body = $outputText
Send-MailMessage @email

I can define most of my parameters for Send-MailMessage up front and once I have the data available for my Body, I can add that into my $email hash table before calling Send-MailMessage and supplying the hash table into the cmdlet (using “@” to splat) and the command will run just like usual.

Enough with $ErrorActionPreference

I have seen a lot of people using $ErrorActionPreference up front in their submissions as a catch all for error handling. Sure, some people are using Stop or Continue (this is already the default). This should never be touched at all in a script unless there is an excellent reason to do so (so far there hasn’t been a reason to do so). Each cmdlet has a common parameter called –ErrorAction that accepts Continue, Inquire, Stop and SilentlyContinue that you can use. Unless you need to suppress error messages for some reason, then I would recommend using Stop and put this command in a Try/Catch statement to handle any errors that come up.

Format* fail

The last thing that I want to go over is the use of Format* cmdlets as a final step to output data to the user. Bad, bad idea! What you are doing is ruining the object for the user in a way that they cannot manipulate the output either through Sort or Where-Object and cannot even output this to a file ( it can be outputted to a .txt file, but it will be just like what is on screen, truncated and all).

Get-Service | Format-Table | Sort Name

image

Doesn’t work out so good, does it? If only the Format-Table wasn’t there, then I could have sorted this out.

Maybe this will work for a CSV file instead?

Get-Service | Format-Table | Export-Csv -NoTypeInformation Report.csv

image

No errors, so it had to have worked correctly, right? Wrong!

image

If you can read this, then you are a better person than me. Smile The same goes with Format-List as well. Format* cmdlets should never be used in a script and should be left for a user to make the decision themselves. It will save you the hassle of being asked why the output stinks and also save you from losing points in a submission.

That’s it for now. Hopefully you can use these tips not only for the Scripting Games but also for “real world” scripts in your environment as well!

Posted in 2013 Scripting Games Judges Notes, powershell, Scripting Games 2013 | Tagged , , | 3 Comments