Author Posts

January 31, 2018 at 12:33 am

Hi All,

I wrote a piece of code and I'd like for the $app results to come out a bit cleaner. It gets cut off if there are too many results. I've tried doing an Out-String, Format-Table, Format-List.. but no dice 🙁 Below is my code. If you run it against an object that you have a lot of, say Microsoft, you'll see what I'm referring to with the output being funky. I gave an example at the end by calling the function. I'm not looking for a flat out answer. Just a nudge in the right direction 🙂


Function Get-InstalledApp {     
    
    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact='low')]
    param(

    [parameter(Mandatory=$true,ValueFromPipeline=$true)]
    [string]$App,

    [string[]]$ComputerName,

    [string]$ErrorLog = "C:\Users\$env:USERNAME\Desktop\AppError$(Get-date -Format MM.dd.yy).txt",

    [switch]$LogErrors

)

begin { Write-Verbose "We will now begin collecting computers with the application that you have selected" }

process {

    Write-Verbose "The process of collecting computers with your selected app will now begin"
    Write-Verbose "Please use two asterisks if you're looking to filter multiple results. Example *Microsoft*"

    TRY {   
            #Calling the -Class and -ComputerName parameter
            $WMIObject_Params = @{
                                  'Class'='Win32_Product'
                                  'ComputerName'=$ComputerName
                                 }

                $EverythingOK = $true
                Write-Verbose "Starting query now"
                #Using Where-Object to do a wildcard search on apps you specify
                $WMIResults = Get-CimInstance @WMIObject_Params | 
                              Where-Object {$_.Name -like "*$App*"}
            
            #Creating custom objects for a table    
            $ObjectProperties =  @{
                                    'ComputerName'=$ComputerName
                                    'AppName'=$WMIResults.Name
                                  }
            $Object = New-Object -TypeName psobject -Property $ObjectProperties
            Write-Output $Object          
               
    } CATCH {
        $EverythingOK = $false
        Write-Warning "The script failed. Please review the logs located on your desktop"
        IF($LogErrors) {
        #Specifying the first error that occurs in the error log
        $ErrorOccured[0] | Out-File $ErrorLog }

     Write-Verbose "The process is now complete. For any error messages, please go to your desktop and search for the ErrorReport log"

           }#CATCH
       }#Process
end{}
}#Function

Get-InstalledApp -App Microsoft -ComputerName $env:COMPUTERNAME

January 31, 2018 at 12:44 am

So, first off, you don't want to run Get-WMIObject against Win32_Product. I've taken out production Sharepoint environments with that. It runs MSIInstaller for each product and can trigger a repair or reset.

You might want to run Get-ItemProperty against this instead: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

What exact properties are you looking for? Also, if you're on a box with PowerShell 3.0 or higher, I'd recommend PSCustomObject as opposed to New-Object PSObject.

*nudge*

January 31, 2018 at 12:53 am

Will,

Funny you mentioned Get-WMIObject. I ended up editing it to run Get-CIMInstance instead, and forgot to update this *oops*.

For the output, I'm looking to get everything. As you can see, if you run it, the results end up cutting off because the result is too large. That's the main part I'd like to fix.

As far as [pscustomobject], I actually had that from the start. However, I switched to New-Object because from what I've read, it may be better for newcomers to PowerShell so it's easier to read. I can change it back though. I tried both, and got the same results.

So you think running it against the registry would be better? Any reason from a technical perspective? Or do you think it just looks cleaner?

January 31, 2018 at 1:06 am

Regardless if you're running Get-CIMInstance or Get-WMIObject, it will initiate MSIInstaller. Try it on a test machine and check your logs. I learned my lesson the hard way using it against a Sharepoint server to look for a particular version of a product. The box fell out of the environment and the Sharepoint team lost their quorum. It required a reload of the box to get it back.

I had to do a lot of explaining to get out of a resume generating event.

You can go either way with PSObject/PSCustomObject. In my opinion PSCustomObject is more readable, but that could be subjective. I do know (at least in older versions of PowerShell) the output of New-Object PSObject didn't necessarily follow how you laid it out in code. Further reading here: https://devops-collective-inc.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/new-object_psobject_vs_pscustomobject.html

January 31, 2018 at 1:36 am

Wow, interesting. I didn't know that. Thank you very much for the knowledge transfer.

I'll definitely give that read! Thank you for your help.