Archive for WMI

Getting the Free Disk Space of Remote Computers Revisited

April 18th, 2017 by and tagged , , , , , , , , ,

Several years ago, I wrote a fairly simplistic script to get the free disk space of remote computers. It wasn’t all that sophisticated, but it got the job that I needed done, so I shared it here on my blog, since I thought others might find it useful. Which, based on the number of hits here, and the comments, they did. However, based on some of those comments, it had a problem for some users.


The problem was that I used Write-Host in it. That was fine for me, because I only used it to write to my screen. But it’s a bad practice to be using Write-Host unless you really need to manipulate screen colours. The reason it’s a bad practice is that it prevents any sort of redirection! This meant that those users who wanted to capture the result of the script in a file were horked, because Write-Host will ALWAYS write to ( … wait for it…  )


The Host. You can’t redirect it. The fix, of course, is easy — use Write-Object instead, which is what I should have done in the first place.


While I was in the process of making that change, I thought it would be nice to add in a basic Get-Help page for it, which was trivial. But then it occurred to me that I really should let it handle pipeline input, allowing me to use other PowerShell commands to select the group of machines I wanted the free disk space on, and then pipe that result directly to Get-myFreeSpace.


Seemed like a good idea, but it turned out I had to almost completely rewrite the script to use the Begin{}/Process{}/End{} syntax. Accepting pipeline input is not as simple as just saying you do in the Parameter statement, you need to actually process that input. The result is the new, improved version of Get-myFreeSpace.ps1 shown below. (If you care about how I got to this script in the first place, do check out the original post, here. There’s some useful information there about the whole process. )


Gets the disk utilization of one or more computers

Get-myFreeSpace queries an array of remote computers and returns a nicely formatted display of 
their current disk utilization and free space. The output can be redirected to a file or other 
output option using standard redirection. 

Gets the disk utilization and free space of all drives on the local host. 

Get-myFreeSpace -ComputerName Server1,Server2
Gets the disk utilization and free space of all drives on the Server1 and Server2

(Get-VM -Name "*server*" | Where State -eq 'Running' ).Name | Get-myFreeSpace
PS C:\>(Get-VM -Name "*server*" | Where-Object {$_.State -eq 'Running').Name | Get-myFreeSpace

Gets a list of running VMs with Server in their name, and passes it to Get-myFreeSpace to process for 
their current disk utilization. The first version of this example uses PowerShell v5 syntax, while 
the second version uses the older syntax that works on earlier versions. 
.Parameter ComputerName
An array of computer names from which you want the disk utilization


    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 26 Nov, 2014 (cpr)
   ModHist: 29 Sep, 2016 -- Changed default to array of localhost (cpr)
          : 18 Apr, 2017 -- Changed to use Write-Output,accept Pipeline,added man page,  (cpr)
     $ComputerName = @("localhost")

Begin {
   if ($Input) {
      $ComputerName = @($Input)
   Write-Output ""
   # Save ErrorActionPreference so we can reset it when we're done
   $eap = $ErrorActionPreference

Process {
   $ErrorActionPreference = 'SilentlyContinue'
   ForEach ( $Computer in $ComputerName ) {
      Write-Output "Disk Utilization for Computer $Computer is: " 
      Get-WmiObject  -ComputerName $Computer -Class Win32_Volume `
         | Format-Table  -auto `
               Expression={"{0:N0}" -f ($_.FreeSpace/1GB)};`
            @{Label="% Free";`
               Expression={"{0:P0}" -f ($_.FreeSpace / $_.Capacity)};`
               Expression={"{0:N0}" -f ($_.Capacity / 1GB)};`
            @{Label="Volume Label";`
      } #EndForEach
} #EndProcessBlock

End {
   # Reset ErrorActionPreference to original value
   $ErrorActionPreference = $eap

And there you have it. A new and improved version of one of the most popular scripts I’ve ever posted here. You can use it to get the disk utilization on your current machine, or any list of remote computers to which you have the rights to run WMI against.


I hope you find this script useful, and I’d love to hear comments, suggestions for improvements, or bug reports as appropriate. As always, if you use this script as the basis for your own work, please respect my copyright and provide appropriate attribution.

Posted in Annoyances, Network Administration, PowerShell, Windows Server, WMI | 4 Comments »

Getting Automatic Services That Are Stopped With PowerShell

September 29th, 2016 by and tagged , ,

One of the first things I check when I am troubleshooting a system is whether all the services that should be running, are. I could just open up services.msc, click on the “Startup Type” column to sort by the startup type, and scroll down through the Automatic services to see which ones aren’t running. But that’s so…. GUI  :p. And slower, and so very one machine at a time. Instead, let’s use PowerShell to make it all easier.


First, I checked Get-Service, thinking it would give me what I need. but it doesn’t. There’s no way with Get-Service to find out what the startup type is — it’s not a property returned by Get-Service. (Yes, I think this is a deficiency. And yes, I expect someday we might get an improvement to Get-Service. But for the moment, we have to work around it. )


Instead, I decided to use the Get-WmiObject cmdlet to find what we need. (If the machine you’re running this from is running PowerShell v3 or later, you can substitute Get-CimInstance for Get-WmiObject. But if you do, you won’t be able to use -Credential.)


Get-WmiObject Win32_Service returns a list of all the services on the local machine. We can extend it with -ComputerName to query the services on a remote computer. And we can filter those services, though the filtering uses WQL as the query language, which is a nuisance since it doesn’t match up to the Filter syntax for the ActiveDirectory module, for example.


To get a list of all the services that should have started automatically, but that are not currently running, on the local machine:

Get-WmiObject -ClassName Win32_Service -Filter "StartMode='Auto' AND State<>'Running'"

But that output is a bit ugly, so we’ll throw some Format-Table at it, and come up with:

Get-WmiObject -ClassName Win32_Service `
              -Filter "StartMode='Auto' AND State<>'Running'" `
             | Format-Table -Auto DisplayName,Name,StartMode,State

Not bad. That gives us an easy to read output with all the information we need. We can wrap that up in a simple cmdlet that assumes the local computer, but that allows us to run it against multiple computers. And we want it to be able to get that list of computer names through the pipeline, of course. Plus, we’ll add a Credential parameter to allow us to run against machines on a different domain, or a workgroup, so long as we provide an appropriate credential.


If we’re going to get output from multiple computers, however, we need to know which one has which services that aren’t running. To do that, we take advantage of Format-Tables GroupBy parameter:

Get-WmiObject -ClassName Win32_Service `
              -Filter "StartMode='Auto' AND State<>'Running'" `
             | Format-Table -AutoSize `
                            -Property DisplayName,Name,StartMode,State `
                            -GroupBy  PSComputer

Now we have everything we need to pull our script together.


Gets a list of stopped services
Get-myStoppedService takes a list of computer names and returns 
a table of the stopped services on that computer that are set to 
automatically start. The default is to return a list on the local computer.
Returns a table of stopped services on the local computer
Get-myStoppedService -ComputerName 'server1','client2'
Returns a table of stopped services on server1 and client2, 
grouped by computer name
.Parameter ComputerName
A list of remote computer names to query. If the current account 
doesn't have permission to query WMI on the remote computer, use 
the Credential parameter to provide alternate credentials. 
The default is the local host.
.Parameter Credential
Standard PSCredential object. Use Get-Credential.
    Author: Charlie Russel
 Copyright: 2016 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 29 September, 2016 (cpr)
     $ComputerName = ".",
     $Credential = $NULL

if ($Credential) {
   Get-WMIObject -ClassName Win32_Service `
                 -Credential $Credential `
                 -ComputerName $ComputerName `
                 -Filter "StartMode='Auto' AND State<>'Running'" `
                | Format-Table -Auto DisplayName,Name,StartMode,State -GroupBy PSComputerName
} else {
   Get-WmiObject -ClassName Win32_Service `
                 -ComputerName $ComputerName `
                 -Filter "StartMode='Auto' AND State<>'Running'" `
                | Format-Table -Auto DisplayName,Name,StartMode,State -GroupBy PSComputerName

Posted in Network Administration, PowerShell, WMI | 2 Comments »

Setting Console Colours

May 30th, 2016 by and tagged , , , ,

As I described in my previous post, I always open both an admin and non-admin PowerShell window when I log on to a computer. To tell the two apart, I set the background colour of the Admin window to dark red, with white text, and the non-admin window to a white background with dark blue text. The result is clearly and immediately different, warning me when I’m running as an administrator. To do that, I use the following:

$id = [System.Security.Principal.WindowsIdentity]::GetCurrent() 
$p = New-Object$id)

# Find out if we're running as admin (IsInRole). 
# If we are, set $Admin = $True. 
if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)){ 
   $Admin = $True 
} else { 
   $Admin = $False 

if ($Admin) { 
      $effectivename = "Administrator" 
   } else { 
      $effectivename = $ 


That works great for older versions of the Windows PowerShell console, but starting with PowerShell v5, that can have an unintended side effect. In PowerShell v5, we now have PSReadLine that does context sensitive colouration of the command line. But the default PowerShell console has a dark blue background, with white text. And when I changed the background colour of my non-admin PowerShell window to white, it gets a little hard to read!! So, to fix that, I use Set-PSReadLineOption to change the various kinds of context sensitive colour changes to something that works with a light background. We don’t want to do that for the dark red background of the Admin window, so we’ll need to check which colour we are and adjust accordingly.

First, get the current colour:

$pData = (Get-Host).PrivateData 
$curForeground = [console]::ForegroundColor 
$curBackground = [console]::BackgroundColor

You’ll only want to configure the context sensitive colouring options if you’re running on Windows 10 or Server 2016. Prior versions of Windows didn’t have the new system console that comes with Windows 10. So you’ll want to check that the build number is > 10240

$Build = (Get-WmiObject Win32_OperatingSystem).BuildNumber

If $Build -ge 10240, then set the various context sensitive tokens to work with the colour we have.

# PowerShell v5 uses PSReadLineOptions to do syntax highlighting. 
# Base the color scheme on the background color 
If ( $curBackground -eq "White" ) { 
      Set-PSReadLineOption -TokenKind None      -ForegroundColor DarkBlue  -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Comment   -ForegroundColor DarkGray  -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Keyword   -ForegroundColor DarkGreen -BackgroundColor White 
      Set-PSReadLineOption -TokenKind String    -ForegroundColor Blue      -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Operator  -ForegroundColor Black     -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Variable  -ForegroundColor DarkCyan  -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Command   -ForegroundColor DarkRed   -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Parameter -ForegroundColor DarkGray  -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Type      -ForegroundColor DarkGray  -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Number    -ForegroundColor Red       -BackgroundColor White 
      Set-PSReadLineOption -TokenKind Member    -ForegroundColor DarkBlue  -BackgroundColor White 
      $pData.ErrorForegroundColor   = "Red" 
      $pData.ErrorBackgroundColor   = "Gray" 
      $pData.WarningForegroundColor = "DarkMagenta" 
      $pData.WarningBackgroundColor = "White" 
      $pData.VerboseForegroundColor = "DarkYellow" 
      $pData.VerboseBackgroundColor = "DarkCyan" 
   } elseif ($curBackground -eq "DarkRed") { 
      Set-PSReadLineOption -TokenKind None      -ForegroundColor White    -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Comment   -ForegroundColor Gray     -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Keyword   -ForegroundColor Yellow   -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind String    -ForegroundColor Cyan     -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Operator  -ForegroundColor White    -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Variable  -ForegroundColor Green    -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Command   -ForegroundColor White    -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Parameter -ForegroundColor Gray     -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Type      -ForegroundColor Magenta  -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Number    -ForegroundColor Yellow   -BackgroundColor DarkRed 
      Set-PSReadLineOption -TokenKind Member    -ForegroundColor White    -BackgroundColor DarkRed 
      $pData.ErrorForegroundColor   = "Yellow" 
      $pData.ErrorBackgroundColor   = "DarkRed" 
      $pData.WarningForegroundColor = "Magenta" 
      $pData.WarningBackgroundColor = "DarkRed" 
      $pData.VerboseForegroundColor = "Cyan" 
      $pData.VerboseBackgroundColor = "DarkRed" 

Finally, let’s make sure that console window is the right size, and while we’re at it, set the window title. (This is a workaround for a PITA bug in recent builds of Windows 10/Server 2016 that seems to have problems setting the console window size and keeping it!)

$host.ui.rawui.WindowTitle = $effectivename + "@" + $HostName +" >" 
$Host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size(120,40)


Now, with all of this, you have effective, context-sensitive, command-line colouring of your PowerShell windows.


ETA: The Console as we know it is completely broken in recent builds of Windows 10. As of August, 2018, you can’t effectively set the background color of the console and have it stick, plus they completely changed the behaviour of Set-PSReadlineOption by changing the parameters, and removing support for -TokenKind with FG/BG colors to totally break this script. Sigh.

Posted in $Profile, Annoyances, Console, PowerShell, Windows 10, Windows Server 2016, WMI | Comments Off on Setting Console Colours

Unmapping Network Drives

June 27th, 2015 by and tagged , , , , ,

Unmapping network drives with PowerShell should be easy, and it is, but with some caveats. If you always create your network drive mappings with New-PSDrive, then it’s easy to unmap them with Remove-PSDrive. But if some of them are created with Group Policy, some of them with the legacy “net use” commands, some of them with New-SmbMapping, and some with New-PSDrive, it’s really not that easy to fully clean up the list of mapped drives. I’ve used a variety of techniques over the years to map and unmap drives, and I’ve finally come to the conclusion that using New-PSDrive and Remove-PSDrive is the cleanest way for most things. However, if you’ve got a mix of mapped drives, created with more than one method, here’s a script to remove them all.

Unmaps network drives
Unmapdrives removes all currently mapped network drives. It's smart enough to 
remove drives mapped with "net use", "New-SmbMapping" and "New-PSDrive". This 
cmdlet accepts no parameters and assumes -Force for all unmappings. 

Unmaps all currently mapped network drives 

    Author: Charlie Russel
 Copyright: 2015 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 06/27/2015 (cpr)

# Build a dynamic list of currently mapped drives
$DriveList = Get-WMIObject Win32_LogicalDisk `
     | Where-Object { $_.DriveType -eq 4 }

# Don't bother running this if we don't have any mapped drives
 if ($DriveList) { 
    $SmbDriveList = $DriveList.DeviceID
 } else {
    Write-Host "No mapped drives found"

Write-host "Unmapping drive: " -NoNewLine
Write-Host $SmbDriveList
Write-Host " "

Foreach ($drive in $SmbDriveList) {
    $psDrive = $drive -replace ":" #remove unwanted colon from PSDrive name
    Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile
    If ( (Get-PSDrive -Name $psDrive) 2>$Null ) {
       Remove-PSDrive -Name $psDrive -Force
Write-Host " "

# Report back all FileSystem drives to confirm that only local drives are present. 
Get-PSDrive -PSProvider FileSystem

This is the simple form of a more generalized script that can accept a parameter of either -All or a list of mapped drive letters to remove. (And yes, unmap isn’t an approved verb. But this script started out life many, many years ago as a batch file (“unmapdrives.cmd”), so it’s still got the same base name it has always had, because that’s what my fingers remember!)

Posted in PowerShell, WMI | 4 Comments »

Getting the Free Disk Space of Remote Computers

November 26th, 2014 by and tagged , , , ,

(For an updated version of this post and script, see Getting the Free Disk Space of Remote Computers Revisited.)

This started out as a simple script to try to get the free space on one of my servers, but I quickly discovered that using WMI’s Win32_LogicalDisk could only give me part of the solution. The catch is that Win32_LogicalDisk doesn’t return the information about volumes that aren’t assigned drive letters. Which is a problem if what you really need to know is how close to out of space your backup disk is! Because Windows Server Backup takes total control of the backup target disk, and doesn’t mount it as a drive letter, you need to use a different WMI Class to get the information you need. After asking some friends (PowerShell MVPs ROCK!), I was pointed to Win32_Volume, which returns all sorts of information about disk volumes whether they are assigned drive letters or not.

The next issue was how to get the actual information I wanted, and then to format it so that it actually made some sense. For example:

(Get-WmiObject –ComputerName Server1 –Class Win32_Volume).FreeSpace


This doesn’t really cut it. OK, let’s try at least getting it into a table:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume | ft –auto DriveLetter,Label,FreeSpace

DriveLetter Label                               FreeSpace 
----------- -----                               --------- 
C:                                            21655351296 
D:          DATA                             103541030912 
E:          EXCHANGE                          75879378944 
F:          FILES                            142417367040 
Y:          New Volume                            5500928 
            Server1 2014_10_15 10:57 DISK_03 565375053824


Well, that’s a bit more useful, but frankly, that number for the backup volume seems big, but is it 500 GB, or 50 GB? At first glance, I have no idea. And if it’s 50 GB, I’m in trouble, but if it’s 500 GB, we’re fine. So, we need to do a bit of manipulation to the output from Format-Table, and the tool for this is to create an Expression that allows you to calculate and format a result in a way that makes more sense. For this, we use an expression as the property to display. So, for example, to display that “565375053824” as Gigabytes, we use:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume `
       | ft –auto DriveLetter,`
                  @{Label=”Free(GB)”;Expression={'{0:N0}’ –F ($_.FreeSpace/1GB)}}
DriveLetter Label                            Free(GB) 
----------- -----                            -------- 
C:                                           20 
D:          DATA                             96 
E:          EXCHANGE                         71 
F:          FILES                            133 
Y:          New Volume                       0 
            Server1 2014_10_15 10:57 DISK_03 527

Now we’re getting somewhere. But what did we do? We use the @{} to tell Format-Table that we were going to use an expression to define a column of data. The Label=”Free(GB)” creates a new column header, and the Expression={“{0:N0}” –F  means we’re going to have a numeric value (including thousands separators) with no decimal values. The calculated value for the column is ($_.FreeSpace/1GB).

So we now have a useful listing of free space on the remote server. Of course, it might be even more useful to know the percentage free. No problem, for that we use the formatting expression “{0:P0}” to express the column as a percentage, and use the calculation of ($_.FreeSpace/$_.Capacity), letting PowerShell do the work of converting that to a percentage. So:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume `
       | ft –auto DriveLetter,`
                  @{Label=”Free(GB)”;Expression={“{0:N0}” –F ($_.FreeSpace/1GB)}},`
                  @{Label=”%Free”;Expression={“{0:P0}” –F ($_.FreeSpace/$_.Capacity)}}
DriveLetter Label                         Free(GB)  %Free
----------- -----                         --------  ----- 
C:                                            20      17 %
D:          DATA                              96      48 % 
E:          EXCHANGE                          71      71 % 
F:          FILES                             133     18 % 
Y:          New Volume                        0       58 % 
            Server1 12014_10_15 10:57 DISK_03 527     51 %


Now we almost have it. Next, it would probably be useful to get the total capacity of the disk while we’re at it, and since I have more than one server, we should probably plan on passing this whole thing an array of computer names. So, the final script, at least for this first pass:

# ********************************************* 
# ScriptName: Get-myFreeSpace.ps1 
# Description: Script to get the free disk space
#            : on a remote computer and display it usefully
# ModHist: 26/11/2014 - Initial, Charlie
#        : 18/04/2017 - Change to Write-Output. 
# *********************************************
Param ([Parameter(Mandatory=$False,Position=0)]
         [String[]]$ComputerName = "Server1")
Write-Output ""
ForEach ( $Name in $ComputerName ) {
   Write-Output "Disk Utilization for server $Name is: "
   Get-WmiObject  -ComputerName $Name -Class Win32_Volume `
      | Format-Table  -auto `
            Expression={"{0:N0}" -f ($_.FreeSpace/1GB)};`
         @{Label="% Free";`
            Expression={"{0:P0}" -f ($_.FreeSpace / $_.Capacity)};`
            Expression={"{0:N0}" -f ($_.Capacity / 1GB)};`
         @{Label="Volume Label";`

You’ll see I tweaked the formatting to right align the calculated expressions, and gave my volume label column some extra space to improve readability. The result is:

Get-myFreeSpace.ps1 –ComputerName “Server1”,”Server2” 
Disk Utilization for server Server1 is:

 Drive Free(GB) % Free Size(GB) Volume Label 
----- -------- ------ -------- ------------ 
   C:       20   17 %      120 
   D:       96   48 %      200 DATA 
   E:       71   71 %      100 EXCHANGE 
   F:      133   18 %      750 FILES 
   Y:        0   58 %        0 New Volume 
           527   51 %    1,024 Server1 2014_10_15 10:57 DISK_03 
Disk Utilization for server Server2 is:

Drive Free(GB) % Free Size(GB) Volume Label 
----- -------- ------ -------- ------------ 
             0   25 %        0 
   D:    1,697   53 %    3,214 Data 
   C:      484   95 %      512


ETA:  Changed to use Write-Output instead of Write-Host. No need for the features of Write-Host, and it was causing issues for those who wanted to redirect the output to a file. This is more flexible.

ETA:  Changed to clean up the problem formatting from the switch in syntax highlighters. Sigh.



Posted in Network Administration, PowerShell, Windows Server, WMI | 33 Comments »

Finding out what RAM DIMMs are installed on a computer without opening the box

September 9th, 2014 by and tagged , , , ,

OK, so I needed to know exactly what RAM was installed on a computer and how many slots there were. I could have had the user shutdown his machine, opened up the box, got out a flashlight, popped out the DIMMs, written down the obscure part numbers, plugged them back in, and closed it up. But not only was that way too much like work, it would totally have disrupted his workday. So, instead, I let PowerShell and WMI do the heavy lisfting. The following script will get the currently installed memory modules, the maximum RAM supported on the computer, and the number slots available.

# Script Name: Get-MemoryModule.ps1
# Script to get the number of memory slots, and the installed memory, on
# the local computer. Could easily be updated to go against remote computers.
# ModHist: 12/29/2012 - initial, Charlie.
#        : 08/26/2014 - Charlie. Added logic to get slots and max RAM.
#        :
$strComputer = "."
$ComputerName = (hostname)
$colSlots = Get-WmiObject `
               -class "Win32_PhysicalMemoryArray" `
               -namespace "root\CIMV2" `
               -Computername $strComputer
$nSlots = $colSlots.MemoryDevices
$nMax = $colSlots.MaxCapacity
$gbMax = $nMax/(1024*1024)
$colModules = Get-WMIObject `
                -class "Win32_PhysicalMemory" `
                -namespace "root\CIMV2" `
                -computername $strComputer
foreach ($objItem in $colModules) {
   Write-host "Bank Label: " $objItem.BankLabel
   write-host "Capacity: " $objItem.Capacity
   write-host "Caption: " $objItem.Caption
   write-host "Creation Class Name: " $objItem.CreationClassName
   write-host "Data Width: " $objItem.DataWidth
   write-host "Description: " $objItem.Description
   write-host "Device Locator: " $objItem.DeviceLocator
   write-host "Form Factor: " $objItem.FormFactor
   write-host "Hot-Swappable: " $objItem.HotSwappable
   write-host "Installation Date: " $objItem.InstallDate
   write-host "Interleave Data Depth: " $objItem.InterleaveDataDepth
   write-host "Interleave Position: " $objItem.InterleavePosition
   write-host "Manufacturer: " $objItem.Manufacturer
   write-host "Memory Type: " $objItem.MemoryType
   write-host "Model: " $objItem.Model
   write-host "Name: " $objItem.Name
   write-host "Other Identifying Information: " $objItem.OtherIdentifyingInfo
   write-host "Part Number: " $objItem.PartNumber
   write-host "Position In Row: " $objItem.PositionInRow
   write-host "Powered-On: " $objItem.PoweredOn
   write-host "Removable: " $objItem.Removable
   write-host "Replaceable: " $objItem.Replaceable
   write-host "Serial Number: " $objItem.SerialNumber
   write-host "SKU: " $objItem.SKU
   write-host "Speed: " $objItem.Speed
   write-host "Status: " $objItem.Status
   write-host "Tag: " $objItem.Tag
   write-host "Total Width: " $objItem.TotalWidth
   write-host "Type Detail: " $objItem.TypeDetail
   write-host "Version: " $objItem.Version

Write-Host -nonewline "Computer $ComputerName has $nSlots Memory Slots, and a Maximum" 
write-host "Memory of $gbMax GigaBytes of RAM" $colModules `
     | ft -auto "BankLabel",@{Label="CurrentMem(MB)";Expression={$_.Capacity/(1024*1024)}},"Speed"

I’ve shown the “local” version of this, but it would be trivial to modify the script to run against a remote computer by adding support for a –ComputerName parameter. Also, at least some computers (most notably laptops) will not report their maximum supported memory correctly. I’ve run this script against the 6 physical computers running here and all worked fine expect for my HP laptop. That has 16 GB of RAM in it, but says the maximum RAM is 8 GB.

Posted in Hardware, PowerShell, WMI | Comments Off on Finding out what RAM DIMMs are installed on a computer without opening the box

Getting Drive Information Using WMI and PowerShell

June 15th, 2013 by and tagged , , ,

In my ongoing quest to remove dependency on legacy DOS commands, I recently created this script to get a list of all the local and mapped drives on a machine and how much free space each has on it.  This script could be easily extended to display additional information (such as the underlying filesystem type) by simply adding to the output tables. All the information is already captured in the Get-WMIObject call at the beginning, but the fun is in the formatting (and math)  of the tables to provide useful information at a glance.

 # Filename: GetDrives.ps1
 # A script to get a list and information on the "local" drives without using "Net"
 # ModHist: 08/05/2013 - Initial
 #                   :
 # ***********************************************************************
 $LogicalDisks = Get-WMIObject Win32_LogicalDisk
 $LocalHDisks = $LogicalDisks | Where-Object { $_.DriveType -eq 3 }
 $RemoteDrives = Get-WMIObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 4 }
 $OpticalDrives = Get-WMIObject Win32_CDRomDrive
$LocalHDisks | ft -auto @{Label="Drive";`
                        @{Label="Volume Label";`
                           Expression={[int]($_.FreeSpace/$_.Size * 100)};`
 $OpticalDrives | Sort Drive | ft -auto `
                           Expression={ if ($_.Capabilities -eq 4) { `
                             "Read/Write" `
                           } else { `
                        @{Label="Disk Volume Label";`
 $RemoteDrives | ft -auto @{Label="Drive";`
                        @{Label="Remote Share";`
                           Expression={[int]($_.FreeSpace/$_.Size * 100)};`


If you find this script interesting or useful, great. And if you have improvements to it, I’d be delighted to hear about them.


Posted in PowerShell, WMI | 1 Comment »

PowerShell v3 – Using PSDrive to Replace Net Use

August 1st, 2012 by and tagged , ,

I routinely have to map drives across domain boundaries, or to/from non-domain and domain machines. In the old days, I used NET USE commands, which were OK, but there were some issues. Besides, it’s time to move to away from legacy commands such as NET. PowerShell v3 includes an updated set of PSDrive cmdlets (Get, New, Remove) that have added the ability to create persistent mappings to a drive letter. Plus, unlike NET USE commands, I can pass a single credential to connect to multiple machines, and prompt for the password. Ah, HA. Now that’s useful. Here’s my drive mapping script for connecting to three different machines with my domain credentials, even though I’m actually connecting from a non-domain joined machine.

# PowerShell script to map drives using New-PSDrive command. 
# Prompts once for credentials, then uses them. Or so we hope. 
# Initial: 10 June, 2012 

# Start by checking for already mapped drives. We’ll use Get-WMIObject to query Win32_LogicalDisk.
# A drivetype of 4 means that the drive is a network drive.

$NetDrives = Get-WMIObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 4 }

# Check which servers have drives mapped to them.
$Srv1Mapped = $NetDrives | Where-Object {$_.ProviderName -match "srv1" } 
$wssMapped = $NetDrives | Where-Object { $_.ProviderName -match "wss-100" }

# Prompt for credentials and store in a variable. 
$Contoso = Get-Credential -Cred "CONTOSO\Charlie" 

# Now, map drives based on that credential 
# First, drives on SRV1. These are general Contoso resources 
if ($Srv1Mapped ) { 
   Echo "Skipping core maps on SRV1" 
} else { 
   New-PSDrive -Name I –root \\srv1\install    -scope Global -PSProv FileSystem -Cred $Contoso –Persist 
   New-PSDrive -Name J -root \\srv1\Download   -scope Global -PSProv FileSystem -Cred $Contoso -Persist 

# Now, shared drives for the home resources 
if ($wssMapped ) { 
   Echo "Skipping Home maps on Windows Storage Server WSS-100" 
} else { 
   New-PSDrive -Name M -root \\wss-100\Music    -scope Global -PSProv FileSystem -Cred $Contoso -Persist 
   New-PSDrive -Name P -root \\wss-100\Pictures -scope Global -PSProv FileSystem -Cred $Contoso -Persist 
   New-PSDrive -Name V -root \\wss-100\Videos   -scope Global -PSProv FileSystem -Cred $Contoso -Persist 

# Finally, some specialized resources 
   New-PSDrive -Name W -root \\srv1\Working     -scope Global -PSProv FileSystem -Cred $Contoso -Persist 
   New-PSDrive -Name U -root \\srv1\Charlie     -scope Global -PSProv FileSystem -Cred $Contoso -Persist 
   New-PSDrive -Name Y -root \\hp180-ts-17\RemoteApps -scope Global -PSProv FileSystem -Cred $Contoso -Persist 

There we go, and I can run this from both elevated and standard user PowerShell windows. The best part is, these mapped drives are visible in that PowerShell window, but also in Windows Explorer, and anywhere else I need a mapped drive.


ETA: We’ve come a long way in Windows PowerShell v5, and there’s a better way to do this. See Mapping Drives Revisited.

Posted in $Profile, PowerShell, WMI | Comments Off on PowerShell v3 – Using PSDrive to Replace Net Use

Stopping All Running Virtual Machines (Hyper-V)

April 6th, 2012 by and tagged , ,

So, a good friend and fellow MVP asked me for a script to shut down all running virtual machines on a server so she could do cold backups of them. This seemed like a perfectly reasonable request, and my first thought was “Well, this gets really obvious and easy in Windows Server 8” since we have a full set of Hyper-V cmdlets there. But then I sort of remembered doing something like this before, and hunted around and found this old TechNet Wiki article I wrote over a year ago. It wasn’t a full fledged script, but had all the pieces I needed to put together a simple script to stop all the VMs on the local Hyper-V host:

# This is a simple script to stop all the currently running VMs on the local
# Hyper-V host. It could easily be extended to accept a command line
# argument of the name of a remote Hyper-V hosts or a list of hosts into an array

$VMs = Get-WmiObject MSVM_ComputerSystem -computer "." -namespace "root\virtualization"
foreach ($vm in $VMs) {
   if ( $ -ne $vm.elementname ) {
      # skip the parent's name
      if ( $vm.EnabledState -eq 2 ) {
         # If the VM is running
         $shutdown = Get-WmiObject MSVM_ComputerSystem `
                        -namespace "root\virtualization" `
                        –query “Associators of {$vm} where ResultClass=Msvm_ShutdownComponent”
         $shutdown.iniateShutdown($true,”System Maintenance”)
         sleep 5

So, what’s happening in that script? Well, Get-WmiObject grags a list of all the VMs on the local Hyper-V Host (-computer “.”), then we simply loop through the list (skipping host itself ($ -ne $vm.elementname), and for each VM that is running ($vm.EnabledState -eq 2), we get a shutdown object for that specific VM and then call the initiateShutdown method on that object.


Note that this is a “forced shutdown”, so is equivalent to “shutdown –s –f” at the command line. Some processes may not get politely shutdown. Too bad, so sad. Since we need this to work regardless of what else is happening, that’s a necessary risk.


ETA: Wow, this is an old script. Written before we had built-in Hyper-V cmdlets to do this. This still works, and isn’t version dependent so far as I know, but I’ve written updated versions of this a couple of times since. The most recent was 2016, here.  You might want to check that post out. It’s a modern, PowerShell v5 version.

Posted in Hyper-V, WMI | Comments Off on Stopping All Running Virtual Machines (Hyper-V)

Starting Exchange Services after a Power Failure

March 15th, 2011 by and tagged , , ,

In my environment, with a virtualized SBS 2011 Standard, there are occasionally Microsoft Exchange 2010 services that don’t properly restart if there has been an abrupt power failure on the Hyper-V host. (Don’t ask.)

Now, of course, the first time this happened, I just logged in to the server and started the services. But when it happened again, it’s time to write a script. And it was a fun script, since it uses WMI and PowerShell remoting and other fun stuff.

# Script to start Exchange services on SBS 2011 Server after power failure
# Accepts a parameter of the exchange server name, but defaults to SRV2 if none entered
# Assumes you are logged in to the domain with Domain Admin credentials
# Created: 14/03/2011 by Charlie
# ModHist: 15/03/11 -switched to using WMI in the session to get StartMode
param ($ExchSrv = "SRV2" )

# first, open a session to the Exchange server
$srv = New-PSSession $ExchSrv

#Now use Invoke-Command with -Session
Invoke-Command -Session $srv -scriptblock {
   $exsvc = gwmi win32_service | Where-Object {$_.Name -like "MsExch*" `
     -and $_.StartMode -eq "Auto"   -and $_.State -eq "Stopped" }
   if ($exsvc ) {
      foreach ($svc in $exsvc ) {
         Start-Service $

The if statement in there is to prevent an error if all the services are running. Of course, for this script to work as it’s written, you’ll need to run it from a workstation in the SBS domain, and you’ll have to enable PowerShell remoting on both the server and the client. If you haven’t done that yet, I’ve posted a quick setup guide on TechNet.

I’ve posted this script up to the Microsoft Script Center, so if you have comments or suggestions to improve it, please comment there.


Posted in Exchange, PowerShell, SBS, WMI | 2 Comments »

« Previous Entries