Packages installed on Node


#1

Greetings Professionals,

Is there any command to pull only the information of what all the packages or software installed on a particular Nodes.

I tried using ohai but it pulls all unnecessary info . need to know only the package name and its version along with its ip and hostname .

Thank you
Prash


#2

Are you wanting Linux or windows information here?

If you are using Windows, you could have a look at this Powershell function, warp it up in a Powershell_Script ruby block as per this documentation.

You haven’t said how you want to collect the data, but putting it into CSV file and placing it in file share might be the go? A bit manual in the end, but yeah…


#3

Yes sir i am looking info for both linux and windows boxes and yes i want those data to be in a CSV file…

Thank you
Prash


#4

Here is a powershell_script chef resource that will list installed applications on a WINDOWS machine it is run and output a CSV file to the location specified in the variable right at the end $OutputFile - If left default on a machine called server01 the path would be C:\OutPut\server01.csv

  machine_name = node['hostname'] # get the machine's DNS name
  powershell_script 'List-Programs' do
    code <<-EOH
        Function Get-RemoteProgram {
        [CmdletBinding(SupportsShouldProcess=$true)]
        param(
            [Parameter(ValueFromPipeline              =$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0
            )]
            [string[]]
                $ComputerName = $env:COMPUTERNAME,
            [Parameter(Position=0)]
            [string[]]
                $Property,
            [switch]
                $ExcludeSimilar,
            [int]
                $SimilarWord
        )

        begin {
            $RegistryLocation = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\',
                                'SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\'
            $HashProperty = @{}
            $SelectProperty = @('ProgramName','ComputerName')
            if ($Property) {
                $SelectProperty += $Property
            }
        }

        process {
            foreach ($Computer in $ComputerName) {
                $RegBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Computer)
                $RegistryLocation | ForEach-Object {
                    $CurrentReg = $_
                    if ($RegBase) {
                        $CurrentRegKey = $RegBase.OpenSubKey($CurrentReg)
                        if ($CurrentRegKey) {
                            $CurrentRegKey.GetSubKeyNames() | ForEach-Object {
                                if ($Property) {
                                    foreach ($CurrentProperty in $Property) {
                                        $HashProperty.$CurrentProperty = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue($CurrentProperty)
                                    }
                                }
                                $HashProperty.ComputerName = $Computer
                                $HashProperty.ProgramName = ($DisplayName = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue('DisplayName'))
                                if ($DisplayName) {
                                    New-Object -TypeName PSCustomObject -Property $HashProperty |
                                    Select-Object -Property $SelectProperty
                                }
                            }
                        }
                    }
                } | ForEach-Object -Begin {
                    if ($SimilarWord) {
                        $Regex = [regex]"(^(.+?\\s){$SimilarWord}).*$|(.*)"
                    } else {
                        $Regex = [regex]"(^(.+?\\s){3}).*$|(.*)"
                    }
                    [System.Collections.ArrayList]$Array = @()
                } -Process {
                    if ($ExcludeSimilar) {
                        $null = $Array.Add($_)
                    } else {
                        $_
                    }
                } -End {
                    if ($ExcludeSimilar) {
                        $Array | Select-Object -Property *,@{
                            name       = 'GroupedName'
                            expression = {
                                ($_.ProgramName -split $Regex)[1]
                            }
                        } |
                        Group-Object -Property 'GroupedName' | ForEach-Object {
                            $_.Group[0] | Select-Object -Property * -ExcludeProperty GroupedName
                        }
                    }
                }
            }
        }
      }
      $PathExists = Test-Path 'c:\\output'
      if ($PathExists -eq $false)
        {
          New-Item -Path 'c:\' -Name Output -ItemType Directory
        }
      else
        {
          # Do nothing
        }

      $OutputFile = ('c:\\output\\'+"$env:COMPUTERNAME"+'.csv')
      Get-RemoteProgram -Property DisplayVersion | Export-Csv -NoTypeInformation -Path $OutputFile
    EOH
    not_if { ::File.exists?("c:\\output\\"+"#{machine_name}")}
  end

Then you are going to need to collect together all the CSV files - I am sure you can have a go at that part :smiley:

Now, on Linux, I would have to do a read up my self - so I can’t help you much there mate…


#5

I’ve expanded the recipe a little. This will now work on windows and linux (tested on CentOS 7.3).

Outputs the .csv file to the root of the systems drive in a folder called output and writes a file named after the DNS name of the machine it is run on.

#
# Cookbook:: list_installed_apps
# Recipe:: default
#
# Copyright:: 2017, SVucich, All Rights Reserved.

# Creates a var with the computers DNS name 
machine_name = node['hostname'] # get the machine's DNS name
# Variable so we can use logic to get the platform 'windows/linux'
op_sys = node['os'] # get the OS type

# Windows work first 
if op_sys == 'windows'
  powershell_script 'List-Programs' do
    code <<-EOH
        Function Get-RemoteProgram {
        [CmdletBinding(SupportsShouldProcess=$true)]
        param(
            [Parameter(ValueFromPipeline              =$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0
            )]
            [string[]]
                $ComputerName = $env:COMPUTERNAME,
            [Parameter(Position=0)]
            [string[]]
                $Property,
            [switch]
                $ExcludeSimilar,
            [int]
                $SimilarWord
        )

        begin {
            $RegistryLocation = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\',
                                'SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\'
            $HashProperty = @{}
            $SelectProperty = @('ProgramName','ComputerName')
            if ($Property) {
                $SelectProperty += $Property
            }
        }

        process {
            foreach ($Computer in $ComputerName) {
                $RegBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Computer)
                $RegistryLocation | ForEach-Object {
                    $CurrentReg = $_
                    if ($RegBase) {
                        $CurrentRegKey = $RegBase.OpenSubKey($CurrentReg)
                        if ($CurrentRegKey) {
                            $CurrentRegKey.GetSubKeyNames() | ForEach-Object {
                                if ($Property) {
                                    foreach ($CurrentProperty in $Property) {
                                        $HashProperty.$CurrentProperty = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue($CurrentProperty)
                                    }
                                }
                                $HashProperty.ComputerName = $Computer
                                $HashProperty.ProgramName = ($DisplayName = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue('DisplayName'))
                                if ($DisplayName) {
                                    New-Object -TypeName PSCustomObject -Property $HashProperty |
                                    Select-Object -Property $SelectProperty
                                }
                            }
                        }
                    }
                } | ForEach-Object -Begin {
                    if ($SimilarWord) {
                        $Regex = [regex]"(^(.+?\\s){$SimilarWord}).*$|(.*)"
                    } else {
                        $Regex = [regex]"(^(.+?\\s){3}).*$|(.*)"
                    }
                    [System.Collections.ArrayList]$Array = @()
                } -Process {
                    if ($ExcludeSimilar) {
                        $null = $Array.Add($_)
                    } else {
                        $_
                    }
                } -End {
                    if ($ExcludeSimilar) {
                        $Array | Select-Object -Property *,@{
                            name       = 'GroupedName'
                            expression = {
                                ($_.ProgramName -split $Regex)[1]
                            }
                        } |
                        Group-Object -Property 'GroupedName' | ForEach-Object {
                            $_.Group[0] | Select-Object -Property * -ExcludeProperty GroupedName
                        }
                    }
                }
            }
        }
      }
      $PathExists = Test-Path 'c:\\output'
      if ($PathExists -eq $false)
        {
          New-Item -Path 'c:\' -Name Output -ItemType Directory
        }
      else
        {
          # Do nothing
        }

      $OutputFile = ('c:\\output\\'+"$env:COMPUTERNAME"+'.csv')
      Get-RemoteProgram -Property DisplayVersion | Export-Csv -NoTypeInformation -Path $OutputFile
    EOH
    not_if { ::File.exists?("c:\\output\\"+"#{machine_name}")}
  end

# Linux work here
elsif op_sys == 'linux'

  directory '/output' do
    owner 'root'
    group 'wheel'
    mode '0755'
    action :create
  end
  bash 'List-Programs' do
    code <<-EOH
      outputfolder='/output/'
      extension='.csv'
      dnsname="#{machine_name}"
      outfile=$outputfolder"#{machine_name}"$extension
      yum list installed | awk '{ print $1,$2 }' | tail -n +1 | sed -e $'1i\\\\\\nPackage,Version' -e 's/ /,/g' | tail -n +1 >> $outfile
    EOH
  end
end

You will still need to build in some way to get the files off the machine. I am sure you could work on that :smiley:


#6

I thought someone said that (at least for Linux) Ohai already does package detection, so you could query node[‘packages’][‘openssl’] for information (or search with knife across multiple nodes). I’m having some hard time finding this but the Ohai packages.rb is listed on https://docs.chef.io/ohai.html so I think I’m just getting the attribute namespace wrong. And looking at https://github.com/chef/ohai/blob/master/lib/ohai/plugins/packages.rb it does appear to support Windows as well.


#7

Checked a different system and I do see this:

This is the chef-shell.
Chef Version: 12.8.1


http://docs.chef.io/

run help' for help,exit’ or ^D to quit.

Ohai2u nclemons@somesystem!
chef (12.8.1)> node[‘packages’]
=> {“accountsservice”=>{“version”=>“0.6.35-0ubuntu7.3”}, “acpid”=>{“version”=>“1:2.0.21-1ubuntu2”}, … (etc)


#8

How does one put that into a CSV file?


#9

Write a simple Ruby script to query the Chef API for the relevant data and store it into a CSV.


#10

Sounds so simple - Examples?