Sunday, January 26, 2014

Getting Install Software in System

Hi,

My name is Ved Prakash. This is my first blog post, please comment and let me know for any suggestions.

I was sitting on my desk and my boss came to me and said, "Hey Ved, We want to know what software are installed on our Servers, so unwanted software can be removed".

Now this type of task is time consuming even for one server as you have to go to Programs and Features and write everything down and  needed to do it for about 20 servers.

I thought for a minute that Powershell could have helped us and I said yes.

So what could be a reliable way to this. We could have used wmi class Win32_Product  but that is not a good way to get installed software as it is not query optimized and change the consistency of software as Windows installer scan each software and not recommended in production environment so answer was Registry.

Under the subkeys of "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" is the place you can get your installed softwares.

I can get a WMI class of type StdRegProv to get the Standard Registry Provider WMI class to access the registry, which can be used on remote systems also using WMI.
Please see that I have used "list"  parameter which will give me Class not Object

$regwmi=get-wmiobject  -Namespace root\default -list -ComputerName $comp -Class StdRegProv

Now I got the registry location and provider to access it now I need to know how to access this registry location.
You can find in below link that below are the value of Registry trees. In my case I need to access HKEY_LOCAL_MACHINE, so I will be using 2147483650 to access registry.
HKEY_CLASSES_ROOT (2147483648 (0x80000000))
HKEY_CURRENT_USER (2147483649 (0x80000001))
HKEY_LOCAL_MACHINE (2147483650 (0x80000002))
HKEY_USERS (2147483651 (0x80000003))
HKEY_CURRENT_CONFIG (2147483653 (0x80000005))
http://msdn.microsoft.com/en-us/library/aa390788%28v=vs.85%29.aspx

Below is the code to get the subkeys.

$HKLM=2147483650
$Regkey="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
$subkeys = $regwmi.EnumKey($HKLM, $Regkey)
$subkeys=$subkeys.snames


Now I know the Registry location, I just need to query these registry value using $regwmi already created.

Now, only location SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall works well if system is 32 bit but for 64 bit OS, additionally we need to dig the values from SOFTWARE\wow64\Microsoft\Windows\CurrentVersion\Uninstall to get the complete list of Softwares installed.

I need to first check my server is 64 bit or 32bit then acording to that I need to call the registry provider with appropriate registry location..

Below is the full code to get the information we are looking for.

  Function get-softwares ($comp,$wow,$bit){
 
   if($wow -eq 32 -and $bit -eq 64)
 
   {
           $wow64="\Wow6432Node"
   }
   else
 
   {
           $wow64=""
   }
   $regwmi=gwmi  -Namespace root\default -list -ComputerName $comp| where{$_.name -eq "StdRegProv"}
    $Uninstallkey="SOFTWARE$wow64\Microsoft\Windows\CurrentVersion\Uninstall"



$regwmi
$subkeys = $regwmi.EnumKey($HKLM, $Uninstallkey)
$subkeys=$subkeys.snames
 
 
foreach($key in $subkeys){

        $thisKey=$Uninstallkey+"\"+$key

    

        $obj = New-Object PSObject

        $obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $comp

        $obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($regwmi.getstringvalue(2147483650,$thiskey,'DisplayName').svalue)

        $obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($regwmi.getstringvalue(2147483650,$thiskey,'DisplayVersion').svalue)


        $obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($regwmi.getstringvalue(2147483650,$thiskey,'InstallLocation').svalue)


        $obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($regwmi.getstringvalue(2147483650,$thiskey,'Publisher').svalue)
      
        $obj | Add-Member -MemberType NoteProperty -Name "InstallDate" -Value $($regwmi.getstringvalue(2147483650,$thiskey,'InstallDate').svalue)
           $obj | Add-Member -MemberType NoteProperty -Name "Type" -Value $wow
        if($obj.installdate -match "[0-9]{8}")
        {
        $obj.Installdate=$obj.installdate.substring(4,2)+"/"+$obj.installdate.substring(6,2)+"/"+$obj.installdate.substring(0,4)}

        $obj
       # Write-host $obj

     #   $array += $obj

  
}
   }
Function Get-InstalledSoftware
{
param(
$Computername='localhost')

 
   $HKLM = 2147483650
 

 
 
$array=@( foreach($comp in $Computername)
{

$cpu=Get-WmiObject win32_Processor -Filter 'DeviceID="CPU0"'  -ComputerName $comp #-cred $cred
$bit=$cpu.AddressWidth
#"a $Comp"
if($bit -eq 64){

get-softwares -comp  $comp -wow 64 -bit $bit
get-softwares -comp $comp -wow 32 -bit $bit
   }
   else{
   get-softwares -computername $compp -wow 32 -bit $bit}
  
})
#$properties=$obj |get-member |  where{$_.membertype -eq "Noteproperty"} | foreach{$_.name}
#[string]::join(",",$properties)
#$values=$obj.properties
$array | Where-Object { $_.DisplayName -ne "" -and $_.displayname -ne $null}
#foreach{[string]::join(",",@(foreach($p in $properties){$_.$p}))}

 }

Above script can be called as  Get-InstalledSoftware -Computername comp1, comp2