At the end of part one, I built a custom Puppet structured fact to relate the Powershell and .NET version information from a Powershell Object to a Ruby Hash that was readable by Facter. The end result was, well, a pile of info.

PS C:\Users\vagrant> facter -p psversiontable
{"psversion"=>{"major"=>4, "minor"=>0, "build"=>-1, "revision"=>-1, "majorrevision"=>-1, "minorrevision"=>-1}, "wsmansta
ckversion"=>{"major"=>3, "minor"=>0, "build"=>-1, "revision"=>-1, "majorrevision"=>-1, "minorrevision"=>-1}, "serializat
ionversion"=>{"major"=>1, "minor"=>1, "build"=>0, "revision"=>1, "majorrevision"=>0, "minorrevision"=>1}, "clrversion"=>
{"major"=>4, "minor"=>0, "build"=>30319, "revision"=>34209, "majorrevision"=>0, "minorrevision"=>-31327}, "buildversion"
=>{"major"=>6, "minor"=>3, "build"=>9600, "revision"=>17400, "majorrevision"=>0, "minorrevision"=>17400}, "pscompatiblev
ersions"=>[{"major"=>1, "minor"=>0, "build"=>-1, "revision"=>-1, "majorrevision"=>-1, "minorrevision"=>-1}, {"major"=>2,
 "minor"=>0, "build"=>-1, "revision"=>-1, "majorrevision"=>-1, "minorrevision"=>-1}, {"major"=>3, "minor"=>0, "build"=>-
1, "revision"=>-1, "majorrevision"=>-1, "minorrevision"=>-1}, {"major"=>4, "minor"=>0, "build"=>-1, "revision"=>-1, "maj
orrevision"=>-1, "minorrevision"=>-1}], "psremotingprotocolversion"=>{"major"=>2, "minor"=>2, "build"=>-1, "revision"=>-
1, "majorrevision"=>-1, "minorrevision"=>-1}}

In this post, I will create a couple of additional facts to crunch the above into something a bit more…legible.

PowerShell Version

On the first line of output above, you can see ‘psversion’ contains a hash with the major, minor, build and revision info for PowerShell. Since PowerShell (and the underlying Windows Management Framework) tend to stick to major releases, I’ll create a fact for PowerShell version using the major and minor numbers from the hash above.

Facter: powershell

Facter.add(:powershell) do
  confine :kernel => :windows
  setcode do
    ps = Facter.value(:psversiontable)
    ps_major = ps['psversion']['major']
    ps_minor = ps['psversion']['minor']
    if ps_major
      "#{ps_major}.#{ps_minor}"
    else  
      "unavailable"
    end
  end
end

In the above example, I’ve:
1. Created a new fact called ‘powershell’.
2. Confined it to Windows hosts
3. In the code block, instead of more powershell, I’m taking the hash that we already have in ‘psversion’ and extracting the numbers I need. In this case, I’m grabbing the major and minor values from the ‘psversion’ nested hash, then pushing them together into a string that resembles a version number. On a Windows 2012 server it should return ‘4.0’ by default.
4. I have it verify that at least something came back for the version info otherwise report the fact as ‘unavailable’.

Fairly simple, yes? You can do the same for the .NET framework, but there’s a bit of math involved. The .NET framework is hiding in the psversiontable hash under the CLRVersion.

"clrversion"=>{"major"=>4, "minor"=>0, "build"=>30319, "revision"=>34209, "majorrevision"=>0, "minorrevision"=>-31327}

This is where your patience can be tested very quickly. I tend to want the version of the .NET framework that corresponds to the available version numbers, for example .NET 2, 3.5, 4, 4.5, 4.5.1, 4.5.2, etc… but you need to decipher what version that is based on the info in this hash. I ended up using a case statement to return a useable string like this:

Facter.add("dotnet") do
  confine :kernel => :windows
  setcode do
    clr = Facter.value(:psversiontable)
    if !clr
      "unavailable"
    else
      major = clr['clrversion']['major']
      revision = clr['clrversion']['revision']
      case major
        when 4
          case revision
            when 1, 269, 276, 296, 544, 1008, 1022, 1026, 2034
              "4.0"
            when 17626, 17929, 18010, 18052, 18063
              "4.5"
            when 18408, 18444, 34014
              "4.5.1"
            when 34209, 90210
              "4.5.2"
          else
            "unavailable"
          end
        when major == 2
          "legacy"
        else
          "unavailable"
      end
    end
  end
end

With the above two facts working off of the psversiontable fact, you should end up with some very usable results:

PS> facter -p powershell
powershell => 4.0

PS> facter -p dotnet
dotnet => 4.5