Mark Minasi's Tech Forum
Sign up Calendar Latest Topics
 
 
 


Reply
  Author   Comment  
jsclmedave

Administrator
Registered:
Posts: 506
Reply with quote  #1 
Using A New CIM Session I can easily check a Windows Server for specific applications and display their DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation.

One of the great things about CIM, as Mike Robbins and Jeff Hicks pointed out, is that I can hit all of the Server OS versions at our company including hundreds of WIN 2000 Servers.

So with the same function I can target a list of Servers with an OS from 2000 to 2012R2.

Note - I do have Local Admin rights on all of the Windows Servers that I am targeting, and they are currently in a single trusted domain...

Once again, Thanks goes out to Jeff Hicks, Mike F Robbins, Richard Siddaway, Adam Bertram and everyone else that worked with me on this...

This part is checking for connectivity to the Target Server and attempting to connect using the DCOM protocol to that Target Server.  If there are any issues it will be written to a file located in the Temp Directory of the PC/Server you are running this from.


       param(
        [Parameter(Position=0,
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullorEmpty()]
        [string[]]$ComputerName = $env:COMPUTERNAME,
 
        [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
    )
 BEGIN {
        $Date = (get-date).ToString('MM-dd-yy-HHmm')
        $Opt = New-CimSessionOption -Protocol Dcom
        $CS = $null
        $SessionParams = @{
            ErrorAction = 'Stop'
        }
        If ($PSBoundParameters['Credential']) {
            $SessionParams.Credential = $Credential
        }
}
#######################################
    PROCESS {
        $AppResults = @()
        ForEach ($Computer in $ComputerName){
        # Checking connection to Server If cannot connect drop this server and go to the next one.
        Try {
            $Check1 = Test-Connection $Computer -Count 1 -ErrorAction Stop | Select-Object Address,IPV4Address
            Write-Host 'Test-Connection successful for Server '  $Check1.Address  $Check1.IPV4Address -ForegroundColor Yellow
            "`r`n"
        } # End Try
        Catch [System.Net.NetworkInformation.PingException]{
            $ErrorMessage = $Computer + "  [CATCH]  " + $Check1.IPV4Address + " " + $_.Exception.Message
            "`r`n"
            Write-Warning -Message $ErrorMessage
            $ErrorMessage | Out-File "C:\Temp\Get-CompanyApps_Errors $Date.txt" -Append
            #Write-Warning -Message "[CATCH] Unable to connect to $Computer. Verify $Computer is online and try again."
            "`r`n"
            Start-Sleep -s 5
            Continue
        } # End Catch
        Try {
            Write-Host -Message "Attempting to connect to $Computer using the DCOM protocol." -ForegroundColor Yellow
            $CS = New-CimSession $Computer -SessionOption $Opt -ErrorAction Stop
        } # End Try
        Catch [Microsoft.Management.Infrastructure.CimException]{
            "`r`n"
            $ErrorMessage = $Computer + " - " + $Check1.IPV4Address + " " + $_.Exception.Message + "[CATCH] You may not have the proper access to: " + $Computer + " - " + $Check1.IPV4Address
            Write-Warning -Message $ErrorMessage
            "`r`n"
            $ErrorMessage | Out-File "C:\Temp\Get-CompanyApps_Errors $Date.txt" -Append
            #Write-Warning "[CATCH] You may not have the proper access to $Computer $Check1.IPV4Address"
            "`r`n"
            Continue
        } # End Catch
"`r`n" 
#########################


This part is gathering all of the REG Data on the Target Server. 


#########################
# REG Data
#########################
$REGData = @()
    $HKLM=2147483650
    $REGPaths = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
    $REGcim = Get-CimClass -Namespace root\default -class StdRegProv -CimSession $CS
    Foreach ($REGPath in $REGPaths) {
        Write-Verbose "Querying $REGPath"
    
    $enumArgs = @{hDefKey=$HKLM;sSubKeyName=$REGPath}
 
    $paramHash = @{
     cimclass = $REGcim
     CimSession = $CS
     Name = 'EnumKey'
     Arguments = $enumArgs
    }
 
    $MysNames = Invoke-CimMethod @paramHash | Select-Object -expand snames | Where-Object {$_ -notmatch '(\.)?KB\d+'} 
    $i=0
    $REGData += Foreach ($item in $MysNames) {
 
    $i++
    $KeyPath = "$REGPath\$item"
    Write-Host $KeyPath -ForegroundColor Cyan
    
    #revise paramhash
    $paramHash.Name = 'EnumValues'
    $paramHash.Arguments = @{hDefKey=$HKLM;sSubKeyName=$KeyPath}
    Invoke-CimMethod @paramHash | Foreach {
       #get value Data
       $hash = [ordered]@{Path = $keyPath}
 
       #add a list of known properties
       'Displayname','DisplayVersion','Publisher',
       'InstallDate','InstallLocation','Comments','UninstallString' | Foreach { 
   
       $paramHash.Name = 'GetStringValue'
       $paramhash.Arguments = @{hDefKey = $HKLM ;sSubKeyName=$keyPath;sValueName=$_}
       $value = Invoke-CimMethod @paramhash 
       $hash.Add($_,$($value.sValue))
       } # Foreach Add Lisr of known properties
 
       #write a custom object to the pipeline
       [pscustomobject]$hash
    } # Foreach paramHash subkey name
 
   } # Foreach REGData = Item in MysNames
 
} # Foreach REGPpath in REGPaths
 
 # $REGData | Sort-Object -Property DisplayName # Use if needed to check values.
 
#########################
# END REG Check




Now onto getting just what was specifically asked for from all of the data above.  In this example I am getting the version of IE and checking for specific .NET Versions.


############ IE VERSIONS #############
$IEvalue = ''
$TryIE = ''
$IEenumArgs = ''
$IEparamHash = ''
$IE = ''
#
$IEHKLM=2147483650
$IEREGcim = Get-CimClass -Namespace root\default -class StdRegProv -CimSession $CS
$IEvalue = 'svcVersion'
$IEPath = 'SOFTWARE\Microsoft\Internet Explorer'
$IEenumArgs = @{hDefKey=$IEHKLM;sSubKeyName=$IEPath;sValueName=$IEValue}
$IEparamHash = @{
cimclass = $IEREGcim
CimSession = $CS
Name = 'GetSTRINGvalue'
Arguments = $IEenumArgs
}
$TryIE = Invoke-CimMethod @IEparamHash | Select-Object ReturnValue
If($TryIE.ReturnValue -eq 0){$IE = Invoke-CimMethod @IEparamHash | Select-Object sValue
$IE.sValue
}
Else
{
$IEvalue = 'Version'
$IEenumArgs = @{hDefKey=$IEHKLM;sSubKeyName=$IEPath;sValueName=$IEValue}
$IEparamHash = @{
cimclass = $IEREGcim
CimSession = $CS
Name = 'GetSTRINGvalue'
Arguments = $IEenumArgs
}
$TryIE = Invoke-CimMethod @IEparamHash | Select-Object ReturnValue
If($TryIE.ReturnValue -eq 0){$IE = Invoke-CimMethod @IEparamHash | Select-Object sValue
$IE.sValue
}
Else
{
$IE = ''
}
}

Write-Host 'IE Version: ' $IE.sValue -ForegroundColor yellow
"`r`n" 
#
########## IE VERSIONS END ###########
#
#
########## DOT NET VERSIONS ##########
#
#
############ DOT NET 2.0 #############
$TryDOT20NET = '' # Set to nothing to clear previous Servers
$DOT20NET = ''
$HKLM=2147483650
$DOT20Path = 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727\1033'
$DOT00value = 'Version'
$DOT20enumArgs = @{hDefKey=$HKLM;sSubKeyName=$DOT20Path;sValueName=$DOT00value}
$DOT20paramHash = @{
cimclass = $REGcim
CimSession = $CS
Name = 'GetSTRINGvalue'
Arguments = $DOT20enumArgs
}
$TryDOT20NET = Invoke-CimMethod @DOT20paramHash | Select-Object ReturnValue
If($TryDOT20NET.ReturnValue -eq 0){$DOT20NET = Invoke-CimMethod @DOT20paramHash | Select-Object sValue 
}
Else
{
$DOT20NET = ''
}
Write-Host 'Dot Net 2 Version: ' $DOT20NET.sValue -ForegroundColor yellow
"`r`n" 
############ DOT NET 3.0 #############
$TryDOT30NET = '' # Set to nothing to clear previous Servers
$DOT30NET = ''
$HKLM=2147483650
$DOT30Path = 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup'
$DOT00value = 'Version'
$DOT30enumArgs = @{hDefKey=$HKLM;sSubKeyName=$DOT30Path;sValueName=$DOT00value}
$DOT30paramHash = @{
cimclass = $REGcim
CimSession = $CS
Name = 'GetSTRINGvalue'
Arguments = $DOT30enumArgs
}
$TryDOT30NET = Invoke-CimMethod @DOT30paramHash | Select-Object ReturnValue
If($TryDOT30NET.ReturnValue -eq 0){$DOT30NET = Invoke-CimMethod @DOT30paramHash | Select-Object sValue 
}
Else
{
$DOT30NET = ''
}
Write-Host 'Dot Net 3 Version: ' $DOT30NET.sValue -ForegroundColor yellow
"`r`n" 
############ DOT NET 3.5 #############
$TryDOT35NET = '' # Set to nothing to clear previous Servers
$DOT35NET = ''
$HKLM=2147483650
$DOT35Path = 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5\1033'
$DOT00value = 'Version'
$DOT35enumArgs = @{hDefKey=$HKLM;sSubKeyName=$DOT35Path;sValueName=$DOT00value}
$DOT35paramHash = @{
cimclass = $REGcim
CimSession = $CS
Name = 'GetSTRINGvalue'
Arguments = $DOT35enumArgs
}
$TryDOT35NET = Invoke-CimMethod @DOT35paramHash | Select-Object ReturnValue
If($TryDOT35NET.ReturnValue -eq 0){$DOT35NET = Invoke-CimMethod @DOT35paramHash | Select-Object sValue 
}
Else
{
$DOT35NET = ''
}
Write-Host 'Dot Net 3.5 Version: ' $DOT35NET.sValue -ForegroundColor yellow
"`r`n" 




 

Now I am taking everything that was found and assigning them to my variables.  If nothing was found it will be set to N/A...


######################################
# Get Application Values
######################################
# $Something = $REGData | Where-Object{$_.DisplayName -Like '*Something*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$RACEAGENT = $REGData | Where-Object{$_.DisplayName -Like '*Server Automation RACE Agent*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$CAAutosys = $REGData | Where-Object{$_.DisplayName -Like '*CA Workload Automation AE*' -and $_.UninstallString -Like 'MsiExec*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$CANumbsoft = $REGData | Where-Object{$_.DisplayName -Like '*Numbsoft*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$TrendDSA = $REGData | Where-Object{$_.DisplayName -Like 'Trend Micro Deep Security*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$BBCPatrol = $REGData | Where-Object{$_.DisplayName -Like 'BBC Performance Assurance*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$BBCNetworker = $REGData | Where-Object{$_.DisplayName -Like 'NetWorker'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$BBCAvamar = $REGData | Where-Object{$_.DisplayName -Like '*BBC Avamar*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$BBCDPA = $REGData | Where-Object{$_.DisplayName -Like 'BBC Data*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$PowerPath = $REGData | Where-Object{$_.DisplayName -Like '*PowerPath*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$NetMon = $REGData | Where-Object{$_.DisplayName -Like 'Microsoft Network Monitor*' -and $_.DisplayName -notlike '*NetworkMonitor Parsers*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
$DellOpenMGR = $REGData | Where-Object{$_.DisplayName -Like 'Dell Open*'} | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation
#$BBCDPA = $BBCDPA
$IE =  $IE
$DOT20NET = $DOT20NET
$DOT30NET = $DOT30NET
$DOT35NET = $DOT35NET
$DOT40NET = $DOT40NET
# If Value Not Found
$NotFound = 'N/A'



Then I am creating my New Objects for display.



# Creating Results Array
$AppResultsArray = @()
# Creating new Objects
  $Obj = New-Object -TypeName PSObject
# If Obj statments - If found will show information, if not will show Extension N/A.
    $Obj | Add-Member -MemberType NoteProperty -Name 'Server' -Value ($Computer)
  
        If ($RACEAGENT)
  {
    $Obj | Add-Member -MemberType NoteProperty -Name 'Blade Race Version' -Value ($RACEAGENT.DisplayVersion -join "; ")
  }  Else {
        $Obj | Add-Member -MemberType NoteProperty -Name 'Blade Race Version' -Value ($NotFound)
    }
    If   ($DOT20NET)
  {
  $Obj | Add-Member -MemberType NoteProperty -Name 'DOT NET 2 Version' -Value ($DOT20NET.sValue)
  }   Else {
    $Obj | Add-Member -MemberType NoteProperty -Name 'DOT NET 2 Version' -Value ($NotFound)
    }



Writing to the screen so I can see what I am getting and expecting in the CSV file.


# Write the output to the screen.
  
    $AppResultsArray += $Obj
    $AppResults += $AppResultsArray
    $AppResults
#########################
# END Report Results
#########################




Cleaning up the CIM Session for Each Target Server and sending the info to a CSV file with a Date Stamp.  I am also passing this to Grid View.  You can remove that if not needed.  I do that for management mostly.  They like pretty stuff...


#########################
# Clean Up At The End        
#########################
    If($CS){
        Get-CimSession | Remove-CimSession
        $CS.Close()
        $CS = $null
    }    
    Else{
    Write-Host 'No CIM SESSION FOUND'
    }
        } # End ForEach Computer
     $AppResults | Export-CSV "C:\Temp\Get-CompanyApps $Date.CSV" -NoTypeInformation
     $AppResults | Out-GridView -Title "Registry Listed My Companies Apps For: $Computer"    
    } # End Process
End{}




And that's it...


I have attached the entire PowerShell script as a TXT file.  Get-CompanyApps.txt
txt Get-CompanyApps.txt     


Again Thanks goes out to Jeff Hicks, Mike F Robbins, Richard Siddaway, Adam Bertram.








__________________
Tim Bolton @jsclmedave
Email: [string](0..20|%{[char][int](32+('527377347976847978324785847679797514357977').substring(($_*2),2))}) -replace ' '  

New to the forum? Please Read this
0
JeffHicks

Avatar / Picture

New Friend (or an Old Friend who Built a New Account)
Registered:
Posts: 35
Reply with quote  #2 
You really should just have the script write the objects to the pipeline. The best practice is not include formatting or things like exporting in the script. You've locked yourself it. What if you needed a different path for the CSV file? Or maybe you need the results as XML? Or you want to filter a bit more after the command completes and then export or convert? You have no flexibility.

If the function just writes to the pipeline then you can run
$date = #some code
Get-CompanyApps | Export-CSV "C:\Temp\Get-CompanyApps  $date.CSV" -NoTypeInformation

If you know you always want the CSV and Out-Gridview options, then write a separate script to invoke the function and generate the output you need. But the function itself should write objects to the pipeline.

__________________
Jeff Hicks Author ~ Trainer ~ Guru
Cloud and Datacenter Management MVP



0
jsclmedave

Administrator
Registered:
Posts: 506
Reply with quote  #3 
Thanks Jeff...

In this case the format and a CSV file is exactly what they wanted including an N/A if it was blank.


But I will look into redoing it to write to the pipeline instead.


I am about to post my next script that is using CIM to pull Server Info which is somewhat similar.

They are working great for what was needed, but I don't mind going back to improve on them.

__________________
Tim Bolton @jsclmedave
Email: [string](0..20|%{[char][int](32+('527377347976847978324785847679797514357977').substring(($_*2),2))}) -replace ' '  

New to the forum? Please Read this
0
JeffHicks

Avatar / Picture

New Friend (or an Old Friend who Built a New Account)
Registered:
Posts: 35
Reply with quote  #4 
I know you want the CSV file. All I'm suggesting is to break what you have now into two parts, with a few minor revisions. Create the function that writes objects to the pipeline. Then write a second script that calls the function and creates the CSV file and gridview output. Later you may need a script that runs your function and creates an HTML report. You don't have to touch your original function, just run the necessary command or create a new script.
__________________
Jeff Hicks Author ~ Trainer ~ Guru
Cloud and Datacenter Management MVP



0
jsclmedave

Administrator
Registered:
Posts: 506
Reply with quote  #5 
Thank you clarifying for me Jeff!

I was making it WAY to hard...

What he was talking about was removing the Export-CSV & OutGridView from the function altogether.

So IF I wanted a CSV then pipe the info from the script to the CSV or GridView


Get-CompanyApps | Export-CSV "C:\Temp\Get-CompanyApps.CSV" -NoTypeInformation

Get-CompanyApps | Out-GridView


If I need a datestamp for the CSV then simple run that first -

$Date = (get-date).ToString('MM-dd-yy-HHmm')
Get-CompanyApps | Export-CSV "C:\Temp\Get-CompanyApps  $Date.CSV" -NoTypeInformation


I cannot tell how much effort and time I wasted making this way too hard...  <g>

Thanks Again Jeff!!




__________________
Tim Bolton @jsclmedave
Email: [string](0..20|%{[char][int](32+('527377347976847978324785847679797514357977').substring(($_*2),2))}) -replace ' '  

New to the forum? Please Read this
0
JeffHicks

Avatar / Picture

New Friend (or an Old Friend who Built a New Account)
Registered:
Posts: 35
Reply with quote  #6 
That is exactly what I'm talking about. When you write a function, you don't want to include any formatting, like Format-Table, or exporting or anything else. I have on occasion sorted before writing output to the pipeline but that is ok as it doesn't change the underlying objects. You want to write a function that you can use in the pipeline like any other cmdlet.
__________________
Jeff Hicks Author ~ Trainer ~ Guru
Cloud and Datacenter Management MVP



0
Previous Topic | Next Topic
Print
Reply

Quick Navigation:

Easily create a Forum Website with Website Toolbox.