none
Checking File version RRS feed

  • Question

  • Hi

    I'm working on a powershell script that will check if a dll files version is correct on lots of servers. To keep it quite basic, I would like to output a warning in red to the screen. This is what I need.

    This is what I have written so far but not sure what the best path to follow is if anyone can advise.

    $xFile = "\\10.1.2.3\c$\Program Files\Company\Server Modules\DataModule.dll"
    $xFileVersion = (Get-Command $xFile).FileVersionInfo
    
    # Need to put some kind if statement here to check the file version number and warn me if the version is not v1.2.3
    

    Much appreciated to anyone who can give me a hand
    Matt

    Thursday, May 8, 2014 11:12 AM

Answers

  • I'm not sure where you got that 'get-command'. What you need is get-childitem.

    example:

    $file= 'C:\Program Files\Realtek\Audio\HDA\BlackBlueSkinImages64.dll'
    $ver=(Get-ChildItem$file).VersionInfo
    
    if($ver.FileVersion -ne'1.0.0.208')
    {
       Write-Warning "File version not correct"
    } 


    Regards, Paul www.servercare.nl



    Thursday, May 8, 2014 12:25 PM
  • Nope.  It is Get-Item for a single file:

    if( (Get-Item '\\10.1.2.3\c$\Program Files\Company\Server Modules\DataModule.dll').VersionInfo.FileVersion -ne '1.0.0.0'){
         Write-Host 'Not match' -BackgroundColor Red
     } 

    We can also use the type:

    ([IO.FileInfo]'c:\windows\system32\pdh.dll').VersionInfo.FileVersion

    But what happens if the file is not found?


    ¯\_(ツ)_/¯


    • Edited by jrv Thursday, May 8, 2014 12:52 PM
    • Marked as answer by jmatty2000 Thursday, May 8, 2014 3:53 PM
    Thursday, May 8, 2014 12:46 PM
  • WMI is a more reliable method:

    function Test-FileVersion{
         Param(
              [Parameter()]
              [string[]]$computer=$env:Computername,
              $filename='c:\windows\system32\pdh.dll',
              $version='6.1.7601.17514'
         )
    
         $fileName=$filename.Replace('\','\\')
    
         if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){
              if($file.Version -ne $version){
                   Write-Host 'Versions don''t match!' -fore red 
              }else{
                   Write-Verbose 'Versions match'
              }
         }else{
              Write-Verbose 'File not found'
         }
    }
    
    Test-FileVersion -verbose
    Test-FileVersion -verbose -version 1.2.3.4


    ¯\_(ツ)_/¯



    • Edited by jrv Thursday, May 8, 2014 1:15 PM
    • Proposed as answer by Paul Weterings Thursday, May 8, 2014 3:43 PM
    • Marked as answer by jmatty2000 Thursday, May 8, 2014 3:53 PM
    Thursday, May 8, 2014 1:10 PM

All replies

  • $xFile = "\\10.1.2.3\c$\Program Files\Company\Server Modules\DataModule.dll"
    $xFileVersion = (Get-Command $xFile).FileVersionInfo
    if ($xFileVersion.FileVersion -ne "1.0.0.0")
    {
    #do anything you want for example
    Write-Host "Not match" -BackgroundColor Red
    }


    MCP Windows XP / MCP ISA 2004 - Não esqueca de avaliar a resposta!

    Thursday, May 8, 2014 11:27 AM
  • I'm not sure where you got that 'get-command'. What you need is get-childitem.

    example:

    $file= 'C:\Program Files\Realtek\Audio\HDA\BlackBlueSkinImages64.dll'
    $ver=(Get-ChildItem$file).VersionInfo
    
    if($ver.FileVersion -ne'1.0.0.208')
    {
       Write-Warning "File version not correct"
    } 


    Regards, Paul www.servercare.nl



    Thursday, May 8, 2014 12:25 PM
  • Nope.  It is Get-Item for a single file:

    if( (Get-Item '\\10.1.2.3\c$\Program Files\Company\Server Modules\DataModule.dll').VersionInfo.FileVersion -ne '1.0.0.0'){
         Write-Host 'Not match' -BackgroundColor Red
     } 

    We can also use the type:

    ([IO.FileInfo]'c:\windows\system32\pdh.dll').VersionInfo.FileVersion

    But what happens if the file is not found?


    ¯\_(ツ)_/¯


    • Edited by jrv Thursday, May 8, 2014 12:52 PM
    • Marked as answer by jmatty2000 Thursday, May 8, 2014 3:53 PM
    Thursday, May 8, 2014 12:46 PM
  • !nope: Get-ChildItem works as well as Get-Item for this.

    OP did not state that the file might not exist.

    I would suggest 'test-path', but you may know of another way, that's the nice thing about PowerShell, it is very versitile.


    Regards, Paul www.servercare.nl

    Thursday, May 8, 2014 1:05 PM
  • WMI is a more reliable method:

    function Test-FileVersion{
         Param(
              [Parameter()]
              [string[]]$computer=$env:Computername,
              $filename='c:\windows\system32\pdh.dll',
              $version='6.1.7601.17514'
         )
    
         $fileName=$filename.Replace('\','\\')
    
         if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){
              if($file.Version -ne $version){
                   Write-Host 'Versions don''t match!' -fore red 
              }else{
                   Write-Verbose 'Versions match'
              }
         }else{
              Write-Verbose 'File not found'
         }
    }
    
    Test-FileVersion -verbose
    Test-FileVersion -verbose -version 1.2.3.4


    ¯\_(ツ)_/¯



    • Edited by jrv Thursday, May 8, 2014 1:15 PM
    • Proposed as answer by Paul Weterings Thursday, May 8, 2014 3:43 PM
    • Marked as answer by jmatty2000 Thursday, May 8, 2014 3:53 PM
    Thursday, May 8, 2014 1:10 PM
  • Hi Everyone

    Thanks everyone so far for all your help.

    For my needs, I know that the file will definitely exist - theres no question about that for my situation. If the file doesn't exist, then these will be very rare and I will just manually copy the file across.

    Whichever way I check the file version, ideally it needs to be the fastest command possible. Sometimes, I might need to check over 200 servers at once. Remotely checking 1 file version can take between 0 seconds and 10 seconds - I would say averaging about 8 seconds. If I need to check more different file versions in the future (quite likely for me) then speed is important.

    So whichever method runs the fastest please.

    Cheers

    Matt


    • Edited by jmatty2000 Thursday, May 8, 2014 1:13 PM
    Thursday, May 8, 2014 1:12 PM
  • Hi Everyone

    Thanks everyone so far for all your help.

    For my needs, I know that the file will definitely exist - theres not question about that for my situation. If the file doesn't exist, then these will be very rare and I will just manually copy the file across.

    Whichever way I check the file version, ideally it needs to be the fastest command possible. Sometimes, I might need to check over 200 servers at once. Remotely checking 1 file version can take between 0 seconds and 10 seconds - I would say averaging about 8 seconds. If I need to check more different file versions in the future (quite likely for me) then speed is important.

    So whichever method runs the fastest please.

    Cheers

    Matt

    THe existence is not at question. A file canbe not found even if it exisits.  Error management is a way to avoid ambiguous outcomes.

    While Get-ChildItem works it can be misleading. 

    WMI allows access to the exact undecorated version number and makes access to many computers very easy.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:15 PM
  • The function can take an array of computers.  With minor adjustments it can take computers from the pipeline.  With another simple adjustment it can generate a complete report.

    No changes take more than one line of change.

    Setting up a solution correctly can save a lot of wasted time and avoid undetected errors.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:17 PM
  • WMI file version checking should take no more than a second on a modern network.

    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:19 PM
  • Matt,

    Have you considered having the servers to run a PowerShell script locally & forward the information to a central location? This way you would be able to have a number of servers work in parralel.

    (if speed is of importance, that may speed up things considerably)


    Regards, Paul www.servercare.nl

    Thursday, May 8, 2014 1:20 PM
  • I just ran that function against a list of computers on a slow network. Less than a second average per computer. We can use an array and add a job command. I can do 600 computers in less than a minute.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:22 PM
  • That seems pretty fast indeed. I agree WMI is the way to go for this.

    Regards, Paul www.servercare.nl

    Thursday, May 8, 2014 1:24 PM
  • Hi Paul / jrv

    I would run the PS script locally on multiple servers - however I don't think my company would allow this to happen due to a lack of resources in the Testing Department - despite it being a really good time-saving/cost reducing idea.

    The other dilemma is that I don't really understand WMI - even though it looks really cool and sounds like it would run a million times faster :(

    Cheers

    Matt

    Thursday, May 8, 2014 1:25 PM
  • Matt,

    Have you considered having the servers to run a PowerShell script locally & forward the information to a central location? This way you would be able to have a number of servers work in parralel.

    (if speed is of importance, that may speed up things considerably)


    Regards, Paul www.servercare.nl

    Why complicate this.  PowerShell canrun parallele jobs from onemachine. Just add -asjob to theWMI call and it willstage the group as a collection onf parallel jobs.  THe result will be aggregated.

    Of course the function isnotsetupfor jobs but can be.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:39 PM
  • Well you now have all of the technology and a complete solution.  Learning how to use Windows and how to administer it is now up to you.

    The function does everything you need for as many servers as you want to use it on.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:42 PM
  • Amazing stuff - one last question before I try to learn more WMI as I've never touched it before.

    What doe the command in bold mean please:-

         if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){

    Cheers

    Matt

    Thursday, May 8, 2014 1:44 PM
  • I was working up to something like that, thinking about remoting & possibly workflow.

    Matt you might want to look at this: http://blogs.technet.com/b/heyscriptingguy/archive/2012/12/26/powershell-workflows-the-basics.aspx

    Sounds however that we seem to be shooting somewhat too high. I believe it is important to Matt to have a solid understanding of what he's working with, so keeping things simple is important.

    Matt; you'll find that jvr's function, even though using WMI is pretty straightforward & does what you want. -

    -

    I'd suggest not using Write-Host though as it doesn't write to the pipeline, but to the console. This means that when you redirect your output it will miss that part of the output.


    Regards, Paul www.servercare.nl


    Thursday, May 8, 2014 1:45 PM
  • That is PowerShell. 

    HELP gwmi -full

    help help

    help commands


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:46 PM
  • I was working up to something like that, thinking about remoting & possibly workflow.

    Matt you might want to look at this: http://blogs.technet.com/b/heyscriptingguy/archive/2012/12/26/powershell-workflows-the-basics.aspx

    Sounds however that we seem to be shooting somewhat too high. I believe it is important to Matt to have a solid understanding of what he's working with, so keeping things simple is important.

    Matt; you'll find that jvr's function, even though using WMI is pretty straightforward & does what you want.


    Regards, Paul www.servercare.nl

    Let the poor guy learn PowerShell and WMI before killing him with an advanced concept like workflow parallelism.

    Just running and understanding the function will pave the way to workflow.  In this case this function can be easily wrapped in a workflow but needs a couple of simple tweeks to make it compatible.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 1:49 PM
  • Someones got to get annoyed!!! lol

    But the output from the WMI script just give me :-

    VERBOSE: Versions match

    Versions don't match!

    I would have expected it to output one or the other ???

    Cheers

    Matt

    Thursday, May 8, 2014 2:08 PM
  • You didn't use fuzzy logic did you? ;-)

    Regards, Paul www.servercare.nl

    Thursday, May 8, 2014 2:11 PM
  • Someones got to get annoyed!!! lol

    But the output from the WMI script just give me :-

    VERBOSE: Versions match

    Versions don't match!

    I would have expected it to output one or the other ???

    Cheers

    Matt

    Someone needs to learn the basics of computers.  What do you think the -verbose is for?  Try using help in PowerShell to learn the basics.  Try changing the command arguments to see what they do.

    You need to think about what you are doing and you need to put in the effort to learn more than just how look helpless. 


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 2:12 PM
  • On a more helpful note... you have simply copied the function provided. The last two lines execute the function.

    The first one uses the defaults in the script the second one sends an argument (the version number you want to check)

    It will be more clear to you if you change it somewhat:

    Write-Host 'Versions don''t match!' -fore red 
    $file.Version
    $version
    as now you can actually see what is compared

    6.1.7601.17514


    Regards, Paul www.servercare.nl

    Thursday, May 8, 2014 2:20 PM
  • Thank Paul

    I just managed to work out the last two lines!!! And then I thought to myself that it was a fair point that jrv said.

    Nearly there....

    I altered the script to check a remote server but it keeps saying File not Found - can I have some more help with this last part of the puzzle please.

    clear
    
    function Test-FileVersion{
         Param(
              [Parameter()]
              [string[]]$computer=$env:Computername,
              $filename='\\10.1.2.3\c$\Program Files\Server\test.dll',
              $version='2011.01.18.1130'
         )
    
         $fileName=$filename.Replace('\','\\')
    
         if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){
              if($file.Version -ne $version){
                   Write-Host 'Versions don''t match!' -fore red 
              }else
              {
                   Write-Verbose 'Versions match'
              }
         }else{
              Write-Verbose 'File not found'
         }
    }
    
    Test-FileVersion -verbose
    #Test-FileVersion -verbose -version 1.2.3.4

    Cheers very much

    Matt

    Thursday, May 8, 2014 2:25 PM
  • Haha.. jrv was right about the basics...

    take a good look at what you're doing with $filename, $computer and the gwmi call....


    Regards, Paul www.servercare.nl

    hint: computer is an argument in the function, but you changed the function itself...

    more hints: the values you see in the param part of the function are simply some default values, you do not have to change anything.

    Just call the function correctly. You'll even find that (thanks to that param part) it 'knows' about the -version and -computer and -filename arguments when you use tab-completion.

    p.s. again:not related, but change that Write-Host to a Write-Output. I know it's not red, but it is better.


    Thursday, May 8, 2014 2:34 PM
  • Hi Paul

    I've adjusted it and think I'm on the right lines but ...

    clear
    
    function Test-FileVersion{
         Param(
              [Parameter()]
              [string[]]$computer="10.1.2.3",
              $filename='c$\Program Files\Server\test.dll',
              $version='2011.01.18.1130'
         )
    
         $fileName=$filename.Replace('\','\\')
    
         if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){
              if($file.Version -ne $version){
                   Write-Output 'Versions don''t match!' -fore red 
              }else
              {
                   Write-Verbose 'Versions match'
              }
         }else{
              Write-Verbose 'File not found'
         }
    }
    
    Test-FileVersion -verbose
    #Test-FileVersion -verbose -version 1.2.3.4

    but get error:-

    gwmi : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

    At line:13 char:15

    +      if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){

    +               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException

        + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

    I've checked and the Remote Procedure Calls are running on my PC and the Server ok.

    A bit more help please.

    Cheers

    Matt

    Thursday, May 8, 2014 2:55 PM
  • Test-FileVersion -verbose -version 1.2.3.4 -computer 10.1.2.3 -filename 'c$\Program Files\Server\test.dll'

    assuming that computer exists


    Regards, Paul www.servercare.nl


    p.s. you did do a 'winrm quickconfig' on the target server right? 
    Thursday, May 8, 2014 3:27 PM
  • Still get the same kind of error when running that command :-

    gwmi : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

    At line:15 char:15

    +      if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){

    +               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException

        + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand


    VERBOSE: File not found

    Thursday, May 8, 2014 3:35 PM
  • Have you checked the firewall rules to ensure that you're allowed to run remote WMI queries?

    Don't retire TechNet! - (Don't give up yet - 12,830+ strong and growing)

    Thursday, May 8, 2014 3:37 PM
  • Hi Mike

    The Servers I check have their own 3rd Party IT Support and would not be willing to alter their firewall settings just so I can check some file versions.

    Interestingly, it works fine if use a command like this so it looks like I'll need to stick to using something like this-- but this command just takes longer to run (8 seconds instead of 0.00001 seconds) :-

    $xFileVersion=(Get-Command$xFile).FileVersionInfo

    Cheers

    Matt

    Thursday, May 8, 2014 3:51 PM
  • I'd recommend first checking the VersionInfo property. Sometimes FileVersion will lie to you.

    (Get-Item 'C:\Program Files\7-Zip\7z.exe').VersionInfo


    Don't retire TechNet! - (Don't give up yet - 12,830+ strong and growing)

    Thursday, May 8, 2014 3:55 PM
  • The VersionInfo property is actually capable of returning an accurate file version, but you have to combine four subproperties:
    - FileMajorPart
    - FileMinorPart
    - FileBuildPart
    - FilePrivatePart

    You could compute the version with those each time you need it, but I update the type information for all file objects so that an extra 'PSFileVersion' property is added to all files (so the PS type system does the work for me). On PSv3, you can simply run the following command, and all files will have that property:

    Update-TypeData -TypeName System.IO.FileInfo -MemberType ScriptProperty -MemberName PSFileVersion -Value {
        if ($this.VersionInfo.FileVersion) {
           [version] ("{0}.{1}.{2}.{3}" -f this.VersionInfo.FileMajorPart,
               $this.VersionInfo.FileMinorPart,
               $this.VersionInfo.FileBuildPart,
               $this.VersionInfo.FilePrivatePart)
        }    
    }
    On PSv2, you'd need to use a ps1xml file.

    The WMI solution works great, but here's an alternate one that can be used with Get-Item and/or Get-ChildItem (if you don't update the type information above, you could create a property with select-object that builds this property).

    If you can access remote systems through admin shares, then you could do something like this:
    $FileName = '\\{0}\c$\Program Files\Company\Server Modules\DataModule.dll'
    $ExpectedVersion = '1.0.0.0'
    
    # This will only output files where the version isn't at least $ExpectedVersion
    # You can change -lt to -ne if you’re looking for the version to be exactly the same
    echo computer1, computer2, computer3 |
    	foreach { Get-Item ($FileName -f $_) | select fullname, psfileversion } |
    	where { $_.PsFileVersion -lt $ExpectedVersion }
    

    or this:

    $FileName = '\\{0}\c$\Program Files\Company\Server Modules\DataModule.dll'
    $ExpectedVersion = '1.0.0.0'
    
    # This will output an object for each computer that has a Pass property that's either true/false
    echo computer1, computer2, computer3 | foreach {
        $ObjectProps = @{
           Computer = $_
           FileName = $FileName -f $_
        }
    
        if ((Get-Item ($FileName -f $_)).PSFileVersion -lt $ExpectedVersion) {
           $ObjectProps.Pass = $false   
        }
        else {
           $ObjectProps.Pass = $true
        }
    
        New-Object PSObject -Property $ObjectProps
    }

    Of course, you could modify this to be a function that allows you to feed computer names to it. You could also change the first command in the pipeline (echo) to any other command that will give you a list of computer names.



    Thursday, May 8, 2014 3:57 PM
  • The WMI error is because the file does not exist.  You need to catch the error and mark the output as file not found.

    WMI cannot be blocked on domain computers without causing issues.

    You were also having issues usingtheremote share so I suspect that you have network issues which nether method will overcome.

    Stick with WMI and trap the error.  You are almost there.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 4:42 PM
  • Test-FileVersion -verbose -version 1.2.3.4 -computer 10.1.2.3 -filename 'c$\Program Files\Server\test.dll'

    assuming that computer exists


    Regards, Paul www.servercare.nl


    p.s. you did do a 'winrm quickconfig' on the target server right? 

    WMI does not require remoting.  Workflow does.  PSRemote does. 

    The issues are purely broken systems.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 4:43 PM
  • Change this line :

    if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer){

    to

    if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer -ea 0){

    This will trap the error and note it in the output.


    ¯\_(ツ)_/¯

    Thursday, May 8, 2014 4:45 PM
  • Hi jrv

    Thanks for your perseverence as I was almost giving up on the WMI method.

    If I try this, I get File not Found. If you can help - the file definitely exists on my own PC c: drive in my public folder which is shared on the network.

    clear
    
    function Test-FileVersion{
         Param(
              [Parameter()]
              [string[]]$computer="PC156",
              $filename='public\apircl.dll',
              $version='6.1.7600.16385'
         )
    
         $fileName=$filename.Replace('\','\\')
         write-host $computer "  " $filename
         #Read-Host $test
    
    if($file=gwmi cim_datafile -filter "Name='$filename'" -Computer $computer -ea 0){
    
              if($file.Version -ne $version){
                   Write-Output 'Versions don''t match!' -fore red 
              }else
              {
                   Write-Verbose 'Versions match'
              }
         }else{
              Write-Verbose 'File not found'
         }
    }
    
    Test-FileVersion -verbose

    Any more clues or help please.

    Cheers

    Matthew

    Friday, May 9, 2014 8:00 AM
  • Test-FileVersion -Filename 'c:\folder\apircl.dll' -computer 'mypc' -version '6.1.7600.16385'

    You cannot use a sharename with WMI.  You must use the full absolute pathname of the file.


    ¯\_(ツ)_/¯


    • Edited by jrv Friday, May 9, 2014 8:47 AM
    Friday, May 9, 2014 8:46 AM
  • Might not be able to use WMI then as I only know the IP addresses of the remote servers. When I try this, it just return File not found  even though the file exists :-

         Param(
              [Parameter()]
              [string[]]$computer="10.50.51.52",
              $filename='c:\public\apircl.dll',
              $version='6.1.7600.16385'
         )

    Friday, May 9, 2014 9:03 AM
  • Might not be able to use WMI then as I only know the IP addresses of the remote servers. When I try this, it just return File not found  even though the file exists :-

         Param(
              [Parameter()]
              [string[]]$computer="10.50.51.52",
              $filename='c:\public\apircl.dll',
              $version='6.1.7600.16385'
         )

    Stop changing the code and use the arguments.  You must use  the actual path to the file's location.  Where is it stored.  "public" is a share name.  On windows 7 it is located under C:\Users\Public\Documents 

    You really need to learn an bout Windows and computers/networking.  These are all basic required admin items.

    Each share points to a folder on the machine.  You need to use the folder path.

    Test-FileVersion -Filename 'c:\users\public\documents\apircl.dll' -computer '10.50.51.52' -version '6.1.7600.16385'

    You can use wmi to get the share path:

    gwmi win32_share -computer 10.50.51.52 |select name, path

    There are many reasons for not using direct access through an admin share or share.  Those reasons will only become obvious over time.  The only drawback to all is that you must be an admin and you mist be a trained admin.  Guessing will take forever and likely cause failures.



    ¯\_(ツ)_/¯


    • Edited by jrv Friday, May 9, 2014 9:20 AM
    Friday, May 9, 2014 9:18 AM