Measure-Command Gotcha!

The short version of this post if that if you use a RETURN statement inside a Measure-Command block expecting the script to return to the command prompt that won’t happen. The next line of code after the Measure-Command block will run instead which definitely won’t be what you were expecting. Skip to the examples if you like….

I was recently working on a script and wanted to store in the logs how long some parts of the script were taking to run. I thought it might be useful therefore to warp parts of the code in a Measure-Command block and store the time taken to a variable that I could then write into the log. Over time I could easily see if the script was slowing down and specifically if any section was slowing down by comparing the log files.

This seems like a good idea and it probably still is but there is a gotcha that I didn’t know about. Most of my script run inside of Active Roles and Quest / One Identity state not to use the Exit command in any of your custom scripts. In fact if you use exit in your scripts the Powershell ISE closes completely when debugging so I’m guessing a similar thing happens to the ARS command shell which is why they say not to use it.

I used to use Throw statements to end all my ARS schedule task scripts so that it would update the task last run message. When I had an ARS issue recently though, Quest support wanted me to clear all the errors from the logs when my scripts run. I wrote a function to update the last run message so I got the same functionality and then changed the throw statements to a Return statement. As I expected this didn’t resolve the issue I was having with ARS but that’s another story entirely.

Anyway back to the Measure-Command block gotcha – if we take a look at this simple script:

Write-Host ‘start’
Return
Throw ‘Stopping script no other lines should run’
Write-Host ‘still here’

The throw statement and the last write-host is never reached as expected but if you wrap it in a measure command block to see how long the script took

Clear-Host
Measure-Command {
Write-Host ‘start’
Return
Throw ‘Stopping script no other lines should run’
}
Write-Host ‘still here’

Throw will be skipped as expected because it’s after the Return statement but the last line of code will run and print ‘still here’ to the console.

I guess this is logical if you think about it but it’s still a gotcha!

Advertisement

Converting Password Last Set to Daylight saving

The password last set attribute pwdLastSet is actually stored as a ‘large integer’ which represents the number of 100 nanosecond intervals since January 1, 1601 (UTC).   You can find a full explanation on the Microsoft doc web page https://docs.microsoft.com/en-us/windows/win32/adschema/a-pwdlastset

It’s easy to get this value converted to a [datetime] object using powershells Get-Date commandlet but what you may not have noticed is that this time is in UTC and if you probably need it to display in local time, including Daylight Saving.  All you need to do is is use the ‘AddHours’ method of a [dateTime] object and add an hour to the time.  The problem is that we are not always in Daylight saving so how do we figure that out.  Fortunately lots of people have already posted how to determine if you are in Daylight Saving or not.  Here is the Microsoft link https://gallery.technet.microsoft.com/Get-DSTInfo-Determine-Time-8f7f9f91 to a Get-DSTInfo function.

Here is an example way of using this function.

Set a variable using the Get-DSTInfo function like this $dayLightSaving = if ( (get-date) -ge (Get-DSTInfo).DaylightChgDate ) { 1 } else { 0 } now we have a variable that it set to 1 or 0 depending on if we are in DST or not.  Now when we get the user objects from AD we can simply create a custom attribute like this:

Get-ADuser <userName> -properties pwdLastSet | select-object sAMAccountName,,@{n=’Password Last Set’;e={ get-date -Date (Get-Date $_.pwdLastSet).AddHours($dayLightSaving)}}