PowerShell Function To Get Time Since A User’s Password Was Last Changed

Here’s a small function I put in my PowerShell profile to tell me how long it’s been since an AD user’s password was last changed. You do know how to change your PowerShell profile, don’t you? Just type the following in a PowerShell prompt.

That will open your PowerShell profile in Notepad. You might be asked to create one if you don’t have anything there yet. Then just save that and next time you open PowerShell, whatever code you have in your profile will be executed. The code I’m putting in there right now is the definition for this function.

It’s pretty straight forward. My function is named Get-TimeSinceLastPWSet and takes one parameter, the username of the user we’re interested in. On Line 10, the actual work gets done. I’m making a new TimeSpan object assigned to $tsSinceLastPWSet which is the time between the user’s Passwordlastset AD attribute and the current date/time.

Since the function returns a timespan object, you can manipulate it like this to get more friendly output. (More info on Composite Formatting from MSDN. No PowerShell examples but it looks a lot like the C#.)

This will give you output that simply looks like “10 days, 12 hours” instead of the generic list formatted output you get when you write out a timespan object. I’ve actually made that the default behavior of the function I put in my personal profile because that’s more valuable to me.

Mine looks like this.

Just a small tweak. It returns that nice-to-look-at-string instead of the timespan object.


Detecting An Exchange Management Shell Connection

You don’t log onto an Exchange server via RDP and open the Exchange Management Shell application when you want to do Exchange-PowerShell things, do you? You follow the steps in my Opening A Remote Exchange Management Shell post, right?

But how do you detect if if you have an open remote connection or not? Well there’s a bunch of different ways so here’s an easy one. First, though, we need to understand a couple things about what happens when you open a remote Exchange Management Shell connection.

Here’s what the output of my Get-Module cmdlet looks like before I do anything Exchange-y.

Get-Module before anything Exchange related

Get-Module before anything Exchange related (click for larger)

I’m in ISE, I have the AD cmdlets added. Nothing going on here is too crazy. Now here’s what it looks like after I open a remote Exchange Management Shell connection like I told you how to do in the post linked above.

Get-Module after adding Exchange Management Shell

Get-Module after adding Exchange Management Shell (click for larger)

Notice that the Exchange stuff gets added under a tmp name? And that it’s different every time? That doesn’t exactly make it easy to detect. With the ActiveDirectory cmdlets you can just run Get-Module -name ActiveDirectory and it will either return something or not. Easy. How are you supposed to do that in a predictable, repeatable fashion for Exchange, especially since any other remote shells created to other services in the same manner may also be added with a tmp_ prefix?

In order to figure out how we can determine if we have a module added that belongs to a remote Exchange Management Shell, let’s take a closer look at the tmp module that just got added.

Details of the last module added

Details of the last module added (click for larger)

At first glance, we’re obviously not going to be able to use the Name or Path attributes to identify remote Exchange Management Shell connections. ModuleType, Version, most of the others all look useless for us here. What looks useful, though, is the Description attribute which reads “Implicit remoting for http://my-exchange-server.fqdn/powershell”. That, we can work with. Here’s my code to tell me if I have a module added whose description is for a remote session to my Exchange server.

The code will either return the description of the module if it’s added, or null. You can work with it like this.

Check it out.

Code at work

Code at work (click for larger)


My August 2015 Scripting Puzzle Solution

If you haven’t heard, PowerShell.org is taking the lead on organizing the PowerShell Scripting Games. There’s a new format that involves monthly puzzles. Here’s their post on August’s puzzle: http://powershell.org/wp/2015/08/01/august-2015-scripting-games-puzzle/

Here is my solution. The instructions are to get information back from a JSON endpoint (read more about it in the link above).

First things first, here’s how I did the one-liner part.

This brings back exactly what Mr. Don Jones has asked for. I’m using the Invoke-WebRequest cmdlet to make a web request to that IP and converting what’s returned using ConvertFrom-Json. Then it’s just a matter of formatting the output and selecting only the items we care about for this puzzle.

Alright, that wasn’t so bad. How about the next challenge? I wrote the following function.They asked for an advanced function, but I skipped the comment based help and the begin/process blocks. I could clean up how I work with the $IP parameter a bit, but, this is easier to look at and explain.

I’ve declared two parameters, $Attributes and $IP. $Attributes are the attributes we want to return. In our puzzle instructions, we’re asked for Longitude, Latitude, Continent_Code and Timezone but you could use this function to get any of them. By default, the function will return all attributes. $IP is another IP address that we can get data for. If you don’t specify one, the function will retrieve data for the client’s IP. Otherwise, we can get data for an IP that isn’t the one we’re making our request from.

Here are a couple examples of the function in action.

Here, I’m just running the script with no parameters set. It gets all the data back from my IP. I’ve sanitized a lot of the data returned for the purpose of publishing this post but it was all returned correctly.

Here, I asked for the attributes from the puzzle and specified the IP address for PowerShell.org. You can see that it returned exactly what we’d expect.

Finally, the challenge asks us to hit another public JSON endpoint. I don’t have a favorite but found one that shows you your HTTP request information. Here is what it looks like in action.

Interesting user agent.


How Do You Tell If Two Directories Have The Same Permissions?

The title of this post is a bit funny. The answer is obviously “You can pop both folders open in Windows Explorer, right click, Properties and compare the security tab!” right? Well, you can, but what about folders that have a lot of complicated permissions? What if you want to compare 100 folders? I don’t know about you but I’m not opening 100 folders and comparing the permissions on them all manually. If only PowerShell could help us! Well it can.

In this example, I have three subdirectories in my c:\temp folder. They’re named test1, test2 and test3. Test1 and test2 have the same permissions but test3 has different permissions than the first two.

The first command to get familiar with is the Get-ACL command. ACL stands for Access Control List. This command may take different objects as parameters but one type of object is a path to a directory. Do a Get-ACL someDirectory | Get-Member and you’ll see the huge number of methods and properties that get returned. We’re only really interested in one property though, the Access property.

Look at that. A list of all the different permissions on the folder we care about! Now all we have to do is compare this ACL to the ACLs of other directories. For this, why not simply use the Compare-Object cmdlet? Here’s the full script to compare three folders and commands. I’ll break it all down.

The first three lines are just getting the ACLs for the three directories I care about and storing the values in variables. There’s tons of better ways to get and organize that information but this way lays it out nicely for this example.

Line 5 is our first Compare-Object command and it’s actually pretty intuitive, in my opinion. It’s taking a reference object ($ACLone) and comparing to a difference object ($ACLtwo) and it’s comparing the Access property. This will return the differences between the two ACLs which is totally our goal. The problem is it looks kind of ugly and useless so I pipe the result into a select command. I’m expanding the Access property so you can see exactly which items are different and formatting it as a table.

On lines 5 and 7 you’ll see I’m also selecting a property called SideIndicator. What’s that? Well, when you compare two objects, in addition to seeing a list of differences, don’t you also want to see which object has the different values? SideIndicator is either => or <= depending on which object has the unique value. I’ll explain.

Here is the output of line 7 (comparing two directories with different ACLs). It’s snipped and edited but you’ll see the important parts and yours won’t look too different.

4-6-2015 1-35-28 PM

Click for larger

The side indicator column points left or right. This indicates whether the unique value is on the reference object or the difference object. Arrows pointing left indicate items on the reference object only, arrows pointing to the right indicate items on the difference object only.

There you go! You can use this and scale it to programatically compare file system permissions using PowerShell.

You can download a script I published for this purpose from the TechNet Script Center.


Quick Tip: Searching Exchange Message Tracking Logs (Get Results From Every Server)

When you use the Get-MessageTrackingLog cmdlet, by default, it only searches for messages/events on the server that you’re connected to (see my post on creating connections to Exchange). That’s not great in a multi-server environment. I want results from every server.

My solution is the following.

The Get-TransportService cmdlet gets a list of all the transport servers in your infrastructure. For each of the servers we get back, I’m running the Get-MessageTrackingLog cmdlet and appending the results to a $results variable. I’m taking that results collection and sorting it chronologically.


My July 2015 Scripting Puzzle Solution

If you haven’t heard, PowerShell.org is taking the lead on organizing the PowerShell Scripting Games. There’s a new format that involves monthly puzzles. Here’s their post on July’s puzzle: http://powershell.org/wp/2015/07/04/2015-july-scripting-games-puzzle/

Here’s my solution. I did the extra challenges, as well except for the optional “go obscure, use aliases” challenge. I didn’t submit my solution, I just do them for funsies and to share my solutions on my blog.

The first thing I’m doing is defining an array of computer names which could have come from anywhere. Then, technically on the next line since there’s a semicolon, I have my command. It’s just a Get-WMIObject on the Win32_OperatingSystem class. The ComputerName parameter takes an array which is basically a built in ForEach-Object loop, satisfying the second and third challenge requirements. I pipe the results into a Format-Table command for easy reading but we’re not out of the woods yet. I select the PSComputername, ServicePackMajorVersion and Version properties pretty obviously but the last item, BIOSSerial looks strange.

The SerialNumber property in the Win32_OperatingSystem  WMI object is different than the SerialNumber property in the Win32_BIOS WMI object. The puzzle clearly requests the BIOS serial number, so I created a custom table column which retrieved the BIOS serial number. Technically speaking the only semicolon I use in this solution comes from the custom table command. I’m not counting the one that separates the definition of $Computers from the real command. I could have changed it to look like this, but I thought my solution was more readable.

Good puzzle! I don’t do a ton with WMI or custom tables so this was weird for me, even if it was a relatively simple puzzle.


Get Random Lines From A File (Or Random Files From A Directory… Or Random Item From Any Collection)

Don’t ask me why but I recently had a need to get a random line from a text file. There’s a small piece of strange behavior that I came across with the cmdlet I chose to use: Get-RandomGet-Random does what it sounds like. It’s commonly used for getting random numbers (see this post I wrote a while ago about a gotcha with this behavior) but you can also pass it an input object.

For this example, I have a file in my c:\temp folder named random.txt. It’s contents just look like this.

So since Get-Random includes an -InputObject parameter, I should just be able to do the following, right?

Well, if you were hoping it would be that easy, I’m afraid I’ve got some bad news. Every time you run this command, the InputObject specified is always the value returned.


Well that’s not very helpful for a guy looking for a random line from the file. Turns out that -InputObject is looking for a collection of items, it’s not doing the work of examining the path to the file and extracting the data from it. That’s easy enough to get around. We’ll just do that work ourselves.

There we go. Get a random item from the collection returned by Get-Content C:\Temp\Random.txt. Then you get output like this.


You could get a random file from a directory like this.

Or, indeed, pass any array or hash table. Here’s an example of getting a random property from the $Host variable.






Quick Tip: Strip Empty Lines Out Of A File

Here’s a quick one-liner that will remove all of the blank lines from a file.

The first thing I do is get the content of the input file. This returns an array of each line in the file which I pipe into a foreach-object loop (alias %). In the if block, I’m detecting if the currently evaluated item is null or just white space. If it isn’t, I append it to the output file.


How’s your Windows Server 2003 migration going? Does that question scare you?

Remember 2003? 2003 was a good year. Camera phones got popular, XBox took off, and I was a 14 year old in 9th grade. 2003 was also, obviously, the year that Microsoft released Windows Server 2003. Are you still running it? You shouldn’t be, but I bet lots of you are. That should scare you because in less than six weeks from the time of this post, on July 14, 2015, Microsoft is ending support for Windows Server 2003. If you’re not done your Windows Server 2003 migration to newer operating systems (Windows Server 2012 R2 is an excellent choice), or worse – not even started, you could face some very serious consequences. Let’s answer a few questions you might have about that.

What does it mean to be unsupported?

In case “end of support” isn’t clear, here’s some of the highlights from the long list of concerns outlined in this IDC white paper on why you should upgrade (pdf). There’s tons of reasons but these were the ones that resonated with me.

  • Elimination of security fixes.

Holy smokes. No more patches? For a second that almost sounds like a good thing, right? You’re probably tired of patching servers. But, think of the consequences and implications of that. No more patches is a terrible, scary, awful thing. If I need to tell you why, you may consider a different career than the one that brought you to my blog. If you ever want to pass another audit, you better be receiving and applying security fixes for all your products, especially ones as fundamental as your Windows OSes.

  • Lack of support.

Do you ever call Premier Support? Read Technet blogs or forums? Microsoft is shutting down support for Windows Server 2003 once it hits end of life. If you want help upgrading, you better get it now because after the end of life date, it might be a challenge to get.

Saying “I can put this off, I’m just going to buy extended support!” is the wrong attitude to have. First, you could buy an Egyptian pyramid for the amount of money that extended support is going to cost. Second, all you’re doing is delaying the inevitable. You have to do this. Do it now. It’s going to hurt more to put it off and do it later.

Okay, so there are some good reasons to get off Windows Server 2003 BUT are there any good reasons to get on Windows Server 2012 R2?

There’s tons. Windows Server 2012 R2 came out Q4 2013 and is the result of decades of learning, improvement, technological landscape shifting, development and a bunch of other buzz-verbs that all mean that it’s better. It’s better. Windows Server 2012 R2 is better than Windows Server 2003. Here’s just a few articles that support that statement.

If you look at all, you’ll find thousands more articles, slides, posts, tweets, talks and more on the benefits and features of Windows Server 2012 R2 over its predecessors.

Upgrading is so intimidating. I need help! Where can I get some?

Microsoft has your back on upgrading and migrating. There are lots of guides and articles on these topics but Microsoft has assembled, in my opinion, the best resource hub out there. Did you click that link? It takes you to the page with all the resources. Click one of these links to go to that page. I can’t overstate how important I think it is that you go to this page and read about the resources to help you migrate away from Windows Server 2003. All the links in this paragraph go to the same page. This is the page: https://www.microsoft.com/en-ca/server-cloud/products/windows-server-2003/default.aspx . It’s in your very best interest to go there and check out what’s there. Need the link one more time? Here.

Does it feel like I’m using this subsection of this post to direct you to Microsoft’s page with tons of resources you can use to make your migration possible, if not easy? It’s because I am. There’s tons of other resources out there, too, and they are a simple search away.

I get it. I want to upgrade. I’ve been pushing my organization to upgrade but I can’t seem to get permission. What can I do?

Surely I’ve convinced you of the many great reasons to migrate away from Windows Server 2003 to Windows Server 2012 R2. These arguments make sense for an IT Pro but maybe not for an executive, business people, or sometimes even to a developer. Here are a few of the common ways I see resistance and my suggestions to overcoming them. Of course, every organization’s politics are different and you may need to figure it out yourself.

  • We have App XYZ that only runs on Windows Server 2003. It’s crucial to our business. There’s no new version.

Respectfully, if this is the honest to goodness truth for your organization, you might be on the Blockbuster/Kodak path of sustainability. Read this Wikipedia article on the theory of Diffusion of Innovations. Take special note of chart that describes the different stages: Innovators, Early Adopters, Early Majority, Late Majority, and Laggards. You don’t have to adopt every new innovation that comes across your desk, but if your entire business is dependent on a technology or product that is about to reach end of life, you’re in trouble. You’re already in the laggard stage of the adoption process if you’re still not off Windows Server 2003. Just don’t fall off the chart completely – get migrating!

There comes a point where you’re not upgrading to gain an advantage, but to catch up to competitors who have already surpassed you.

  • App XYZ is crucial to our business. There’s a new version but we can’t afford the down time to upgrade.

This one is easier to work with than the last one. Attack this resistance from two sides. First, reiterate the importance of upgrading and all the bad things that will happen if you don’t. Second, and most importantly, find business reasons that make migrating to Windows Server 2012 R2 or the new version of App XYZ desirable to your specific stakeholders. Often with executives and business groups, it’s even more important to PULL them towards something new as it is to PUSH them away from something old.

To address the downtime concerns, put effort into making a plan that makes the downtime as short and painless as possible. Do a side-by-side migration. Do the cut over at 3 in the morning when your customers are all asleep. Find a way to make the downtime as tolerable as possible.

  • We don’t need new features. We accept the risk of running in an unsupported fashion. It’s just not worth our time to migrate.

This is a naive attitude, in my opinion. If you can’t find a creative way to improve anything within your organization with even one new feature in Windows Server 2012 R2, you’re not looking. A willingness to accept the risk of running unsupported demonstrates a lack of complete understanding of the risk involved with doing so. What would your customers say if you told them that your systems don’t receive security updates any more? If you get resistance like this, you need to find a reason to pull your stakeholders towards the newer technologies and make sure they’re clear on the risks of maintaining status quo.

Alright, I’m ready to take this on! Now how about a summary of some kind?

Glad you asked. If you take anything out of this post, make it these few things.

  1. Being unsupported is bad. Really bad. You don’t want to be unsupported for a lot of reasons including no more security patches.
  2. Windows Server 2012 R2 has a ton of new features that make it a great OS to migrate to.
  3. Microsoft has a lot of resources available to help you upgrade.
  4. Getting stakeholder permission for an upgrade is as much about selling the benefits of moving to a new system as much as it is about the disadvantages of staying on the old one.

Good luck and happy migrating!


Quick Tip: Find All The Mail Enabled Groups A User Is A Member Of

Here’s a one-liner that will help you find all the mail enabled groups that a user is a member of. A little pre-requisite reading is this bit on group types to understand the difference between a security group and a distribution group: https://technet.microsoft.com/en-us/library/cc781446%28WS.10%29.aspx?f=255&MSPPError=-2147217396

Here’s the one-liner!

It might not be the epitome of efficiency but it works and served me well when I needed it to.

First, we’re running a Get-ADUser command on our interesting user and making sure to retrieve the MemberOf property in addition to the standard properties returned. Out of all of the returned properties, it turns out that MemberOf is the only one I’m interested in so I select only that property by wrapping the command in brackets and appending .MemberOf. Second, I’m piping all of the groups that the user is a member of into a foreach-object loop. For each of the objects returned, I’m performing a Get-ADGroup. I have to do this because I can’t necessarily tell which groups the user is a member of are mail enabled just from their name, I have to run the Get-ADGroup command to get more information. I’m piping these results into a where-object command where I select only the groups whose GroupCategory is equal to “Distribution” (see the pre-requisite reading above). Then I format the group names into a table.

I could have got every group in my Active Directory and searched for groups that contained my user as a member and were Distribution types, but in my situation, it was faster to only spot check the groups that the user was actually a member of. I have a lot of groups, you might not.