Use PowerShell to Find A User’s Group Membership

Have you ever needed to find the Active Directory groups a user is a member of? While not immediately visible as a default property of Get-ADUser cmdlet’s output, you can add it using the –Properties argument. For example:

Get-ADUser -Identity Administrator -Properties MemberOf

This adds the MemberOf property to the output object, from the list of available sub-properties:

image

You can extract the list itself using the “dot” operator as follows:

(Get-ADUser -Identity Administrator -Properties MemberOf).MemberOf

image

Using PowerShell to Update a DNS CName

imageAs part of a simple Disaster Recovery strategy, we can use DNS aliases, CNames, to point clients at the currently live service. It seemed like a simple task to use PowerShell to script the update of the CName from one server to another. Unfortunately, that wasn’t the case, as much searching didn’t reveal a simple method to do that update. However, the solution is simple. Continue reading

Getting the Oldest Events from a Windows Event Log

File:PowerShell 5.0 icon.png

I needed a way to get the oldest events in a Windows event log so I could check if we had at least one day’s worth for audit purposes. It turns out to be very simple as the Get-WinEvent cmdlet has the functionality built in. In this example we will retrieve the oldest 10 events from the current computer’s Security log:

Get-WinEvent -LogName Security -MaxEvents 10 –Oldest

The output on my example Domain Controller is:

image

The cmdlet also has a –ComputerName parameter so you can retrieve the events from a remote machine in needed.

Ed Wilson, the Microsoft Scripting Guy, as a good blog post about this cmdlet on the TechNet blogs site: https://goo.gl/9Axh2Y

Counting the Members of an Active Directory Group

File:PowerShell 5.0 icon.png

I was reviewing some very old code I’d written to count how many members a particular Active Directory group contained. I’ll not embarrass myself with the 15 lines of code I’d previously used. Instead we can just marvel at the simplicity of the PowerShell I’d use now:

(Get-ADGroup -Identity “DNSAdmins” -Properties members).members.count

Breaking this down, we have two key points to note. The first is that Get-ADGroup has an “optional” property called members, which is a collection of all members of the named group. I say “optional” only to highlight that it is not supplied to the pipeline as part of the group object by default. This is true of many similar cmdlets which could retrieve a very large number of properties. Such cmdlets usually have a –Properties parameter to add further properties to the pipeline as part of the object.

The second point is that any list or collection will have a count method we can call to find out how many members that collection contains.

So this single line of PowerShell will in this case tell me how many members the DNSAdmins group contains. This might include both individuals and subgroups.

Problems Formatting Numbers

File:PowerShell 5.0 icon.png

In the previous post we looked at formatting a number to round it to a given number of decimal places. While that worked a treat for displaying the number, it does have one significant drawback. It will convert the number to a string. This makes sorting by that number, for example to find the drive with the least space, problematic to say the least. That is, “10” will sort before “2” as strings rather than 2 sorting before 10 numerically.

We can see this in action if we use the GetType() method which our data objects have. For example:

image

You can see in the Name column, the number’s type is a Double, while the formatted string is obviously a String.

A simple way around this is to include both the formatted and unformatted values in your output, displaying one and sorting by the other. It’s just worth noting this behaviour, in case it’s important for your specific situation.

Formatting Numbers

File:PowerShell 5.0 icon.pngNow we have discovered the percentage of free space on my C drive, we can look at making it a little more presentable. At the moment, we use the obvious but brute-force method of calculating a percentage:

Get-Volume -DriveLetter C | Select Size, SizeRemaining, @{ Label=”PercentFree”; Expression={ 100 * ($_.SizeRemaining / $_.Size) } }

image

But the value for PercentFree is not very readable. However, we can use formatting notation to improve this:

Get-Volume -DriveLetter C | Select Size, SizeRemaining, @{ Label=”PercentFree”; Expression={ “{0:P}” -f ($_.SizeRemaining / $_.Size) } }

The expression used to create the percentage field has been simplified somewhat, using the formatting notation “{0:P}” (with the double-quotes), followed by -f and the value we want to format. Breaking this apart, the formatting notation begins with the zero-based argument we want to format (you can use this notation to format multiple arguments). Then we have a “:” followed by the formatting we want to apply. “P” stands for percentage format, defaulting to two decimal places. We could have written “{0:P2}” and this would give the same result:

image

You can find out more about number formatting here: https://technet.microsoft.com/en-us/library/ee692795.aspx

The Microsoft Scripting Guy, Ed Wilson, also did a long article all about this very subject, using this very subject matter, evaluating disk space, so you can find out more in that article here: https://blogs.technet.microsoft.com/heyscriptingguy/2014/10/11/weekend-scripter-use-powershell-to-calculate-and-display-percentages/

Calculating Percentage Disk Free Space

File:PowerShell 5.0 icon.pngHaving used Get-Volume to find the free space on my PC’s C drive, I’d rather like to find out how much free space I have, as a percentage. You’d think we could just do the maths:

image

But as the error shows, $_.Size has evaluated to zero (hasn’t been found). Using the terms without the $_ gives a similar, if more verbose, result:

image

This is where Hash Tables come to the rescue. This simple syntax allows you to create expressions  on the fly. I came across them a while back in this great resource: https://goo.gl/WLqAZx and once you’ve used them you’ll wonder how you ever did without them.

So, in our case we can now write a more useful query to determine the amount of space available on our C drive:

Get-Volume -DriveLetter C | Select Size, SizeRemaining, @{ Label=”PercentFree”; Expression={ 100 * ($_.SizeRemaining / $_.Size) } }

image

The @{ … } syntax has two main parts. The first is the label to be displayed for this new value. This is separated from the second part by a semi-colon. That second part is the expression to be evaluated, which itself is surrounded by braces.

You can use Get-Member to see how the new column of data has been added to the pipeline as part of the object selected:

image

As we can see, the result of the calculation is a Double and a NoteProperty. This makes it like any other field in the object and can be operated on further down the pipeline just like any other field.

Obviously this is not limited to Get-Volume but can be used anywhere a calculation needs to create a new data value in the pipeline.

Get-Volume on Windows 10

File:PowerShell 5.0 icon.png

Yesterday we mentioned that Windows 10 had a new cmdlet for interrogating volume size, Get-Volume. This makes it much simpler to retrieve volume details. So, as an example, to get the C drive’s size we could use:

Get-Volume -DriveLetter C

image

This is a much more intuitive syntax but only available in the latest PowerShell for Windows 10 and Windows Server 2016.

Size of the C Drive

File:PowerShell 5.0 icon.png

It is a fairly common requirement on Windows servers to check how big their drives are and how much free space you have left. You can use the new Get-Volume cmdlet to retrieve these details (amongst other things) and this works great on Windows 10 and Windows 2016. If you need to do the same on other versions of Windows we can use WMI. Specifically, we can use the win32_volume class:

Get-WmiObject -Class win32_volume | ft -AutoSize Name, Capacity, Freespace

image

The first row in the screenshot is a hidden partition on my example laptop for bitlocker drive encryption, while D:\ is my CD drive. C:\ is the drive we are after.

We can use the –filter clause to pass a parameter to select just the drive we want. Looking at the available output (use Get-Member for this, or just list everything using Format-List *), we can use Name, DriveLetter or Caption for this filter. Updating our command we now have:

Get-WmiObject -Class win32_volume -Filter “DriveLetter=’C:'” | ft -AutoSize Name, Capacity, Freespace

image

The format for the filter parameter is the column of interest in the Get-WmiObject cmdlet’s output, equal to the value you are targeting. So in this case I used DriveLetter=’C:’ as it seemed the most appropriate and self-documenting choice. Notice we needed to specify the exact value from the DriveLetter column, which included both the letter “C” and the “:”. Use Format-List * and double check the specific value if necessary.

Getting the Oldest Event in the Security Log

File:PowerShell 5.0 icon.png

The Get-WinEvent cmdlet is very straight forward to use. It even has arguments to obtain events of a specific age, making getting the oldest event in a log really easy. To get the oldest event in the current machine’s Security log for example:

image

The –Oldest argument combined with the –MaxEvents argument allows us to pick a specific number of events from the tail end of the log. In this case the oldest one event.

Get-WinEvent also has a –ComputerName argument, allowing us to target events on another computer. So putting that together with with my previous article, we could get the oldest event from all domain controllers with the following command:

“Domain Controllers”, “Read-Only Domain Controllers” | Get-ADGroupMember | Sort |
%{ Get-WinEvent -LogName Security -Oldest -MaxEvents 1 -ComputerName $_.Name } |
ft -AutoSize MachineName, TimeCreated, LevelDisplayName, TaskDisplayName, Message

image

In case you didn’t know, the % character is an alias for ForEach-Object and will iterate through each item in the pipeline (our domain controllers) executing the script block (the bit in the braces) once per item. We use it here as we need to pass in the -ComputerName parameter which is not accepted from the pipeline unfortunately.