Expert Solution for 2011 Scripting Games Beginner Event 1: Use PowerShell to Determine if a Process Is a Private Build

Expert Solution for 2011 Scripting Games Beginner Event 1: Use PowerShell to Determine if a Process Is a Private Build

  • Comments 6
  • Likes

Summary: Microsoft Windows PowerShell MVP, Thomas Lee, solves 2011 Scripting Games beginner event 1 by finding private build processes.

Microsoft Scripting Guy, Ed Wilson, here. Our expert commentator for Beginner Event 1 is Dr. Thomas Lee. Here is Thomas' scripting autobiography from The Official Scripting Guys Forum! where he is a moderator:

I've been scripting pretty much forever. I used to be pretty hot at JCL on the IBM 360 in the late 1960s, and I did a ton of shell scripting in the 70s on ICL VME. I learned batch scripting with DOS 2.0. I never really grokked VBS (and never got infected with *ix). But I truly “got” Monad when I first saw it in September 2003 and never looked back. I'm proficient in Windows PowerShell 1.0 and 2.0, and specialize in the .NET and WMI aspects. My one interesting fact is that I was the first person to blog about Monad. Check out my Under the Stairs blog and my PowerShell Scripts blog.

Worked solution

Like so many Windows PowerShell scripting problems, this challenge breaks down into two parts: first knowing how and where to look to see if some file or process is a private build; then secondly, writing the script in such a way as to be generic, produce the CSV file that is required, etc. This event also throws up the fact that there are often two ways to do things in Windows PowerShell, if not more.

In my case, I really did not know where to look to see if a file was a private build. From the event scenario, I was tempted to look at the processes; but instead, I started by issuing a very simple query to my main search engine: “Microsoft Private Build.” Interestingly, the first reference was to the System.Diagnostics.Fileversioninfo class—in particular, the Privatebuild property. And when I looked further down the page, it even had a sample that actually checked to see whether Notepad was a private build! Armed with this information, it looked like the script would be fairly easy to write. Basically, I had to run something like the following command on each computer, and then look at whether the IsPrivateBuild property was True (indicating that the file is a private build) or False.

[System.Diagnostics.FileVersionInfo]::GetVersionInfo($file)

But then I re-read the event scenario. The scenario asks for computers that are running the file you are interested in. And that means that in the example, Notepad had to be running. In other words, this might have something to do with the processes on the system after all.

As I thought more about the real life problem (The Scripting Games are all about real life problems, right?), it became clear —a server that has a private build file could result in an application compatibility issue when another conflicting application was deployed. It seemed to me that my boss wanted to find whether a server could run this private build as opposed to whether the private build was running!

Of course in real life, as a beginner scripter, I would have a boss to ask for more guidance (after all, he gave me the task to do, right?). But since this solution is for the Scripting Games, I am my boss! So I approved my approach and moved on. Of course, this means that you might come up with an entirely different answer (and still be “right”). Maybe next time, my boss could write the scenario better!

In developing the solution I first decided to write the script as a function that took a parameter ($file) that would represent the file you are looking for. To facilitate handling multiple computers, I added a second parameter, computer, to contain an array of computers you wanted to search for this file on. I also included a couple of calls to the function to check out that it worked.

The body of the script iterates through all the computers and checks to see if the file existed. To do the checking, I decided to use the great remoting feature in Windows PowerShell 2.0 to send a script (query.ps1) to each computer to query if the file existed. I built the query and then copied it to a local file. I also included in this script a call to hostname to get the host’s actual name (as opposed to some other name, such as a CName or a netbios name, where the hostname is different). I return two items from the function: whether the file is a private build and the actual host’s name.

It turns out that this was a little tricky, and I resorted to a little string manipulation to get the query perfect. There are bound to be more elegant solutions to that problem. Here’s my script:

 

<#

.SYNOPSIS

    This script queries remote machines to find out if

    a private build of a file resides there.

.DESCRIPTION

    This script is a worked solution for the Scripting Games.

.NOTES

    File Name  : Get-PrivateBuildInfo.ps1

    Author     : Thomas Lee - tfl@psp.co.uk

    Requires   : PowerShell Version 2.0

.EXAMPLE

    Get-PrivateBuildInfo -file "C:\windows\notepad.exe" -computer "cookham8"

    Process, computer, PrivateBuild

    C:\windows\notepad.exe, Cookham8, False

.EXAMPLE

    get-privatebuildinfo  -file "C:\windows\notepad.exe" -computer "cookham8","cookham1"

    Process, computer, PrivateBuild

    C:\windows\notepad.exe, Cookham8, False

    C:\windows\notepad.exe, Cookham1, False

#>

Function Get-PrivateBuildInfo {

Param(

   [string]$file,

   [string[]] $computer

)

# Start of function

# Write header output

"Process, computer, PrivateBuild"

# Specify query and store away

$qs  = "`$filver `= [System.Diagnostics.FileVersionInfo]::GetVersionInfo(`"{0}`")`n" -f $file

$qs += "hostname;"

$qs += "`$filver"

$qs | Out-File .\query.ps1

# Run query on each system

$computer | foreach {

  $result = invoke-command -file .\query.ps1 -computername $_

  #output info

  $h   = $result[0]

  $ipb = $result[1].isprivatebuild

  "{0}, {1}, {2}" -f $file, $h, $ipb 

}

}

get-privatebuildinfo  -file "C:\windows\notepad.exe" -computer "cookham8"

get-privatebuildinfo  -file "C:\windows\notepad.exe" -computer "cookham8","cookham1"

 

Thank you, Thomas, for providing this solution for the Beginner Event 1.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Even though this script makes perfect sense in the real world and was also my first idea, it fails to reach one of the main objectives of the scenario. It clearly asks for notepad RUNNING on the machines. And this provided solution checks if notepad exists on the machine and not if it is running.

    Do not misunderstand me, the solution is very good/perfect, but does meet one of the main criterias specified in the Beginner Event 1.

  • Hello Trevor,

    after commenting on your advanced event1 solution, I have to comment again :-)

    CRTL-1 is right, I suppose!

    A very interesting solution, that made me think a bit longer over some details.

    Why didn't the solution use a scriptblock ... ?

    $computer |

       Foreach-Object {

           $error.Clear()

           Invoke-Command -computername $_ -ArgumentList $processName -ErrorAction SilentlyContinue `

           -Scriptblock {

               param ($processName)

               if (($fileVersionInfo = Get-Process $processName -FileVersion)) {

                   "$processName, $env:ComputerName" #, $fileVersionInfo.IsPrivateBuild"

               } else {            

                   "$processName, $env:ComputerName, -- not running --"

               }

           }

           if ($error) { "$processName, $_, -- unknown --" }

       }

    }

    You may get over the potential problem, of not being able to create the file: ".\query.ps1"

    ( btw: Error handling is missing in the presented solution )

    kind regards, Klaus

  • 1) Where error handling?

    2) [System.Diagnostics.FileVersionInfo]::GetVersionInfo - .Net,Why not use Get-Item,Get-Process?

    3) Why not return objects?

    4) Why create a temporary file?

    I have configured the Applocker policy and the script is blocked =)

    Script in one string  =) :

    Invoke-Command {param($file) (get-item $file).VersionInfo} -ArgumentList "C:\Windows\notepad.exe" -ComputerName "Test1","Test2" | `

    Select-Object @{n="Process";e={$_.filename}},@{n="Computer";e={$_.PSComputerName}},IsPrivateBuild

  • using get-childitem notepad.exe works as well.

    If you $f = get-childitem c:\windows\notepad.exe

    then

    $f.versioninfo.isprivatebuild returns false correctly.

    another case of more than one way to powershell I guess.

  • First thanks for all the comments - sorry it's taken me a few days to comment I've been on the road constantly for far too long! I knew the moment I started digging into this sample that I'd come up with some interesting feedback and that my proposed solution would raise some great questions!

    Re CTRL-1 and others. The scenario, like all too many instructions from some of the bosses I've had, apparnetly ask one thing but really seem to ask another. Yes, it says' 'running' but then the brief also says the reason we want to know is because an application we plan to deploy has an issue running on the same machine as this private build of notepad (or at least that's how I read it). So it seemed to me that finding whether a system COULD run the private build was more valuable then just whether it was currently running said private build. I did mention that in real life, I'd have had a boss to ask about this. I was trying to see beyond the simple words in the brief and my boss approved my updated brief as noted in my submission. :-)

    A couple of folks mention error handling. Great point. I left the solution without much error handling simply because I wanted to show a simple-ish solution. In real life, there would have been a bunch of try/catch blocks, etc.

    Re Klaus's suggestion on a script block. Great point. If I were to re-do this today, I'd probably be using the script block. This just goes to show that there are several ways to do anything in PowerShell.

    And rewanlanman's point - yup. That would show it too, but only if the version of notepad running was the one with the private build.

    So to summarise:

    1. There are several ways to carry out the task - and the comments here highlight that well. There are both different broad approaches depending on how you read the brief, but also in the mechanisms you deploy to carry out that brief.

    2. I tried to look beyond the simple brief and to understand the real issue. That issue, in my view, was that the private build of notepad we have running on some machines has a conflict with an application we plan to deploy. So it felt to me I needed to find out where THAT problem could exist, not on only those machines actually running the version of notepad. If you read the brief more narrowly than I did, then there are other, arguably better, ways to achieve this.

    Thomas

  • Something I have to add to any comments I made:

    As I did realize that you and a lot of other judges have been to Las Vegas and as I saw how busy most of you are, I already asked myself: How can you cope with that? ... how can you stand it?

    I would never be able to do all these things in such a short period of time ... I only can offer my highest respect to all of you who still are willing to give us a bit of their time commenting these games!

    ( That said, I hope the games will continue next year :-)

    best wishes, and a happy easter!

    Klaus