Packages installed on Node

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

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ā€¦

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

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ā€¦

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:

1 Like

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.

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)

How does one put that into a CSV file?

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

Sounds so simple - Examples?