Check that driver file versions match on all your cluster nodes via Powershell

This is more of a proof of concept, but I've used it with success internally.  Take it and do with it what you want.  Many thx to Brandon who did the "heavy lifting" when I got stuck!

Overview:

Ever run into cluster issues and wanted to see if the driver file versions matched on all the nodes of the cluster to rule out a mismatch on a driver level?  Well I did!  The basic gist is that you can show all the file versions for each node by just running the script against a node name.  If you want to see only the drivers that don’t match then you’d use the pipeline with where-object (?).

Typical output:

When All drivers match:
PS C:\Debuggers> Test-MSCluster.ps1 ServerSQL11 | ?{!$_.IsSame}
Getting Nodes via WMI
Getting the drivers on: ServerSQL11
Getting the file versions for the drivers on: ServerSQL11
Getting the drivers on: ServerSQL12
Getting the file versions for the drivers on: ServerSQL12
PS C:\Debuggers>

One Mismatch:
PS C:\Debuggers> Test-MSCluster.ps1 ServerAX | ?{!$_.IsSame}
Getting Nodes via WMI
Getting the drivers on: ServerAX
Getting the file versions for the drivers on: ServerAX
Getting the drivers on: ServerBX
Getting the file versions for the drivers on: ServerBX

FileName                                                                   ServerAX                                                                 ServerBX
--------                                                                   ----------                                                                 ----------
rmcast.sys                                                                 6.0.6001.18000                                                             6.0.6001.18069

Many nodes, many mismatches:

PS C:\Debuggers> Test-MSCluster.ps1 Server-Clus--11 | ?{!$_.IsSame} 
Getting Nodes via WMI
Getting the drivers on: Server-Clus--10
Getting the file versions for the drivers on: Server-Clus--10
Getting the drivers on: Server-Clus--11
Getting the file versions for the drivers on: Server-Clus--11
Getting the drivers on: Server-Clus--15
Getting the file versions for the drivers on: Server-Clus--15
Getting the drivers on: Server-Clus--16
Getting the file versions for the drivers on: Server-Clus--16
Getting the drivers on: Server-Clus--13
Getting the file versions for the drivers on: Server-Clus--13
Getting the drivers on: Server-Clus--12
Getting the file versions for the drivers on: Server-Clus--12

FileName   : Dbgv.sys
Server-Clus--10 : 4.60
Server-Clus--11 : FileMissing
Server-Clus--15 : FileMissing
Server-Clus--16 : FileMissing
Server-Clus--13 : FileMissing
Server-Clus--12 : FileMissing
IsSame     : False

FileName   : HpCISSs2.sys
Server-Clus--10 : FileMissing
Server-Clus--11 : FileMissing
Server-Clus--15 : FileMissing
Server-Clus--16 : 6.8.0.64 Build 9 (x86-64)
Server-Clus--13 : 6.8.0.64 Build 9 (x86-64)
Server-Clus--12 : 6.8.0.64 Build 9 (x86-64)
IsSame     : False

FileName   : USBSTOR.SYS
Server-Clus--10 : FileMissing
Server-Clus--11 : FileMissing
Server-Clus--15 : FileMissing
Server-Clus--16 : FileMissing
Server-Clus--13 : 6.0.6001.18000
Server-Clus--12 : 6.0.6001.18000
IsSame     : False

FileName   : mrxsmb10.sys
Server-Clus--10 : 6.0.6001.18000
Server-Clus--11 : 6.0.6001.18000
Server-Clus--15 : 6.0.6001.18000
Server-Clus--16 : 6.0.6001.18068
Server-Clus--13 : 6.0.6001.18000
Server-Clus--12 : 6.0.6001.18000
IsSame     : False

FileName   : nm3.sys
Server-Clus--10 : 03.02.0764.0001
Server-Clus--11 : FileMissing
Server-Clus--15 : FileMissing
Server-Clus--16 : FileMissing
Server-Clus--13 : FileMissing
Server-Clus--12 : FileMissing
IsSame     : False

Code:

 

 

 ######################################################################
#Test-MSCluster.ps1
Param($ClusterNode)

# I am using this hashtable to store a unique list of file names. 
$Files = @{}
# I am using this array to store my custom objects we create later.
$FileObjects = @()

Write-Host "Getting Nodes via WMI"
$nodes = gwmi -q "Select name from MSCluster_Node" -namespace root\mscluster -computername $ClusterNode -Authentication PacketPrivacy | %{$_.Name}

# Here we process each node and get all the drivers from the node and add it to our $Files HashTable to be processed
foreach ( $node in $nodes )
{
    Write-Host "Getting the drivers on:"  $node
    # Here we are getting a list of the .sys files. Notice I am only getting the names
    $filelistFinal = get-childitem "\\$node\admin$\system32\drivers" *.sys | %{$_.name}
    
    Write-Host "Getting the file versions for the drivers on:" $node
    foreach($file in $filelistFinal)
    {
        # foreach file found we add it to the hasttable, but hashtables can only have a key once
        # so we need check if the key already exist. I do this because it is possible you could have
        # unique drivers per node.
        if(!$Files.$file)
        {
            $Files.Add($file,"added")
        }
    }
}

# Ok... now we have all our files time to process the hashtable and create our custom objects
foreach($FileName in $Files.Keys)
{
    # This is how I create an object for each file
    $myFileObj = New-Object System.Object
    
    # This is how we add a property. In this case the FileName property. For these scenarios I chose add-member
    # because you can dynamically add properties (i.e. NodeName with value of File version)
    $myFileobj | add-Member -MemberType NoteProperty -Name FileName -Value $FileName
    
    # Now we need to add properties for each node.
    foreach($node in $nodes)
    {
        # Making sure the file exist on the node
        if(Test-Path \\$node\admin$\system32\drivers\$FileName)
        {
            # Getting ProductVersion Info to use as the value for the Node Property
            $fileInfo = [system.diagnostics.fileversioninfo]::getversioninfo("\\$node\admin$\system32\drivers\$FileName")
            $myFileobj | add-Member -MemberType NoteProperty -Name $node -Value $FileInfo.ProductVersion
        }
        else
        {
            # File not found using FileMissing as the value for the Node Property
            $myFileobj | add-Member -MemberType NoteProperty -Name $node -Value "FileMissing"
        }
    }
    # Outputting Object
    $FileObjects += $myFileObj
}

foreach($result in $FileObjects)
{
    $isSame = $true
    # Getting Server Name from Properties of the custom object
    $servers = $result | Get-Member -MemberType Noteproperty | ?{$_.Name -ne "FileName"} | %{$_.Name}
    
    # Checking the value of each server vs the other servers
    foreach($server in $servers)
    {
        foreach($srv in $servers)
        {
            if($srv -ne $server)
            {
                # If the the value is different we set $isSame to $false
                if($result."$srv" -ne $result."$server"){$isSame = $false}
            }
        }
    }
    # add the isSame property to the object
    $result | add-Member -MemberType NoteProperty -Name IsSame -value $isSame
    
    # output object
    $result 
}
######################################################################

 

***Note:  This script is not fast, as it is getting the file versions for every driver (*.sys)  on each system,  I'd highly suggest not running this over the WAN...