Viewing Memory in PowerShell

Hello there, this is Benjamin Morgan, and I’m a Premier Field Engineer covering Active Directory and Platforms related topics.  This is my first blog post I hope you are all as excited about this as I am! Today I wanted to talk with you about a couple of quick ways for querying system memory (and provide some background as to *why* I was doing this).  Well without further ado let’s get started…

Pre-Amble

Recently I was working with a customer and they had an interesting problem. They were having an issue retrieving specific user attributes from Active Directory using PowerShell. While building the command in a lab the original syntax that I was given was: Get-ADUser -filter * -properties *

Well, as we all know, friends don’t let friends use ” -filter * -properties *” because it will return everything about everything, or in this case everything about all AD users, whereas best practice is to fine tune your scripts so you’re not wasting resources on information that is not needed and will never be used. Needless to say, the script would run for a little bit then bomb out.

My first step was to obviously change the “-filter *” to “-filter ‘name -like “*”‘ but leave the “-properties *” so we could identify the exact attributes that were needed. After this change the script ran a little longer but continuously bombed out. After running the script several times and it continuously failed at the same point, after retrieving user X, my first instinct was that maybe the user right after user X had a corrupted property so I needed to determine what user was next and then see what was going on with their account. I knew that I could do a “for-each” loop, but I didn’t want to take the time to do this since the customer was on a time crunch. I modified the “-properties” statement to only return the attributes that were needed and hoped for the best knowing that if all else failed I would do a “for-each” statement and get the information that way. I then changed the PowerShell command to ‘Get-ADUser -filter ‘name -like “*”‘ -properties PasswordNotRequired’ and that command worked. After looking at the user accounts full properties that were directly after the user that it failed on, and everything returned correctly I knew that there was something else going on, but they had the information they needed so all was good. So, the real question was what was going on and why was it happening.

Before we go on, its important to be aware that the recommendation for domain controllers is to have enough memory to store the dit file in memory, the OS requirements, and enough for any third party applications installed on the DC; in other words, the file size for the dit, plus ~15-20% for the OS (you can read more on this here: https://social.technet.microsoft.com/wiki/contents/articles/14355.capacity-planning-for-active-directory-domain-services.aspx). In this situation, these best practices weren’t followed, so the added burden of parsing all of their users caused the DC to essentially hang and did not allow PowerShell to return all the requested information when using wildcards.

Problem/Solution

I knew that since the AD user attributes were good, it had to be something simple, right? Next, I started looking at the domain controller performance. All I had was PowerShell access with no actual rights to do anything except what was given to me by the PowerShell credentials of the person logged into the computer. (This was a server Admin, not a Domain Admin account…) I decided that since they were a fairly large environment, I might want to check the resources on the domain controller I was connected to. This account had rights to log into the DC! That’s a security topic that is discussed in the PAW solution https://docs.microsoft.com/en-us/windows-server/identity/securing-privileged-access/privileged-access-workstations. I figured that the first resource to look at was RAM. A domain controller should have more memory instead of less, and I knew that PowerShell could pull everything but I wasn’t sure how to get everything that I needed. So off to Bing I went… After some research the best way to get the information was to leverage WMI. The issue I encountered was that WMI returns the results in bytes, which is useless to me. Luckily, PowerShell can do just about anything! It can natively convert bytes to GB, KB, MB, or anything else you may want. I am still trying to figure out how to have it make me coffee in the morning though.

I used the Get-WMIObject cmdlet because the customer was still using Windows 7 with PowerShell 2.0. If you are using PowerShell 3.0 or above then you can use Get-CimInstance which is the more modern way of retrieving this information. The Scripting Guy has a good blog comparing and contrasting Get-WMIObject and Get-CimInstance, https://blogs.technet.microsoft.com/heyscriptingguy/2016/02/08/should-i-use-cim-or-wmi-with-windows-powershell/. The scripts were also ran locally for the purpose of putting together this blog but the commands can be ran via the PowerShell remoting ability of your choice. The following link explains the different PowerShell remoting options, https://technet.microsoft.com/en-us/library/gg981683.aspx.

The first command is Get-WMIObject win32_ComputerSystem which returns an output like the following

Or you can use Get-CimInstance win32_ComputerSystem | format-list which returns an output like the following

So TotalPhysicalMemory doesn’t do a lot of good unless you want to do the conversion to GB yourself but as I said PowerShell is smart enough to be able to do that for me.

According to https://technet.microsoft.com/en-us/library/ee692684.aspx to do the conversion the command is followed by “/ 1GB” so the command would be Get-WMIObject win32_ComputerSystem | foreach {$_.TotalPhysicalMemory /1GB}, but while this command will return the amount of memory in GB it will be in decimal notation and will look like

Get-CimInstance win32_ComputerSystem | foreach {$_.TotalPhysicalMemory /1GB}

To truncate the decimal places and only show whole numbers you have to call the .NET framework’s System.Math class ([math]). That command would be,

Get-WMIObject win32_ComputerSystem | foreach {[math]::truncate($_.TotalPhysicalMemory /1GB)}

Get-CimInstance win32_ComputerSystem | foreach {[math]::truncate($_.TotalPhysicalMemory /1GB)}

While this command returns showing my system only has 3GB, in reality it has 4GB, this is due to truncating the amount of memory that is shown to remove all decimal points and only show whole numbers.

In order to see the exact amount of memory you want to round the TotalPhysicalMemory to the nearest whole number. The command to do this is,

Get-WMIObject win32_ComputerSystem | foreach {[math]::round($_.TotalPhysicalMemory /1GB)}

Get-CimInstance win32_ComputerSystem | foreach {[math]::round($_.TotalPhysicalMemory /1GB)}

Hopefully this helps you hunt down some of those pesky memory issues, and thanks for reading!