Reporting on SCCM Application Deployment Progress with Powershell
By Steve Bowman
Published December 19, 2018
reporting-sccm-powershell
Estimated Reading Time: 3 minutes

I’m going to show you a new approach to getting deployment status out of SCCM.

Most of our readers are familiar with using SCCM Reports, or the SCCM console, to get deployment status when asked.  Usually, when asked, I turn to SQL.  However, I was looking at all the cmdlets that come with SCCM when I realized: There are no cmdlets dedicated to simple reporting.

As such, I took it upon myself to create a simple cmdlet that returns both summary and details of the status of an Application Deployment. I’ll include the code at the bottom of this post, but first, I’ll show you how to use it.

The name of the script is CMReporting.ps1.  For now, all the required functions are within this script.

Using the Script to Get Deployment Status

To use this script, you first need to load the functions into memory by running the script.

This script must be run from the SCCM Site Server.

Here is one example on how to use it:

Get-CMAppDeploymentReport -ChooseFromGrid -DetailType Both -OutputType Text

You are first presented with a gridview list of all current app deployments.  Highlight one and click “OK”.

selectdeployment

 

After about 30 seconds, you’ll see a display like the following in PowerShell:

 

output

As promised, here is the code:

 


Function Get-WindowsErrorMessage {

    [CmdletBinding()]

    param (
        [int]$ErrorCode ,
        [string]$ErrorSource = "Windows",
        [switch]$SimpleOutput
    )

    #This error code was generated from the Windows Exception Library
    If ($ErrorSource -eq "Windows") {
        $ErrorMessage = [ComponentModel.Win32Exception]$ErrorCode
    }


    #Write Output of Function
    If ($SimpleOutput) {
        Write-Output $ErrorMessage
    }
    Else {
        Write-Output "$ErrorSource Error $($ErrorCode): $($ErrorMessage.Message)"

    }
}

Function Get-CMAppDeploymentReport {

    param(
        [ValidateSet("Summary","Detail","Both")]
        [string]$DetailLevel,
        [ValidateSet("Object","Text")]
        [string]$OutputType,
        [int]$AssignmentID,
        [switch]$ChooseFromGrid
        
    )

    #Select Deployment to Report on
    if($ChooseFromGrid) {

        $DeploymentList = Get-WMIObject -Namespace root\sms\site_mp1 -class SMS_DeploymentSummary -Filter "FeatureType = 1"

        $SelectedDeployment = $DeploymentList | Select-Object AssignmentID, CollectionName, SoftwareName, @{Name="DeploymentTime";Expression={$([Management.ManagementDateTimeConverter]::toDateTime($_.DeploymentTime))}},@{Name="DeploymentIntent";Expression={If ($_.DeploymentIntent -eq 1){"Required"}elseif($_.DeploymentIntent -eq 2){"Available"}else{"Unknown"}}},@{Name="EnforcementDeadline";Expression={$([Management.ManagementDateTimeConverter]::toDateTime($_.EnforcementDeadline))}} | Out-GridView -OutputMode Single -Title "Select a Deployment to Report on"


        $AssignmentID = $SelectedDeployment.AssignmentID

    }

    #Get Summary Data
    $Summary = Get-WMIObject -Namespace root\sms\site_mp1 -class SMS_DeploymentSummary -Filter "AssignmentID = $AssignmentID and FeatureType = 1"

    $CollectionInfo = Get-WMIObject -Namespace root\sms\site_mp1 -Class SMS_Collection -Filter "CollectionID = ""$($Summary.CollectionID)"""

    If ($CollectionInfo.CollectionType -eq 2) {$CollectionType = "User"}elseif($CollectionInfo.CollectionType -eq 1){$CollectionType = "Device"}else{$CollectionType = "Other"}

    $SummaryTable = $Summary |Select-Object AssignmentID, CollectionID, CollectionName, SoftwareName, @{Name="DeploymentTime";Expression={$([Management.ManagementDateTimeConverter]::toDateTime($_.DeploymentTime))}},@{Name="DeploymentIntent";Expression={If ($_.DeploymentIntent -eq 1){"Required"}elseif($_.DeploymentIntent -eq 2){"Available"}else{"Unknown"}}},@{Name="EnforcementDeadline";Expression={$([Management.ManagementDateTimeConverter]::toDateTime($_.EnforcementDeadline))}}, NumberSuccess, NumberInProgress, NumberErrors, NumberUnknown, NumberTotal, PackageID

    $SummaryTitle = "Summary of Deployment for $($Summary.SoftwareName) to $($CollectionInfo.MemberCount) $($CollectionType)(s)"

    $DetailTitle = "Detail for Deployment of $($Summary.SoftwareName) to $($CollectionInfo.MemberCount) $($CollectionType)(s)"

    $OutputReport = New-Object -TypeName PSObject

    If ($DetailLevel -eq "Summary" -or $DetailLevel -eq "Both") {

        $OutputReport | Add-Member -MemberType NoteProperty -Name "SummaryTitle" -Value $SummaryTitle
        $OutputReport | Add-Member -MemberType NoteProperty -Name "SummaryResults" -Value $SummaryTable

    }

    If ($DetailLevel -eq "Detail" -or $DetailLevel -eq "Both") {

        $States = Get-WMIObject -Namespace root\sms\site_mp1 -Class "SMS_StateInformation"
        #Get Detail Data

        $Detail = Get-WMIObject -Namespace root\sms\site_mp1 -class SMS_AppDeploymentAssetDetails -Filter "AssignmentID = $AssignmentID"

        $Devices = @()

        Foreach ($Target in $Detail) {

            If ($Target.AppStatusType -eq 5) {
            $ErrorCode = $(Get-WMIObject -Namespace root\sms\site_mp1 -class SMS_AppDeploymentErrorAssetDetails -Filter "MachineID = $($Target.MachineID) and AssignmentID = $AssignmentID").ErrorCode
            $ErrorMessage = Get-WindowsErrorMessage -ErrorCode $ErrorCode
            }
            Else {
            $ErrorCode = ""
            $ErrorMessage = ""
            }

            $CIComplianceInfo = Get-WmiObject -Namespace root\SMS\site_mp1 -Class SMS_CI_ComplianceHistory -Filter "ResourceID = $($Target.MachineID) and CI_ID = $($Target.AppCI)"

            $Device = New-Object -TypeName PSObject
            #$Device | Add-Member -MemberType NoteProperty -Name "ComplianceState" -Value "$(If ($Target.ComplianceState -eq 1){"Compliant"}elseif($Target.ComplianceState -eq 2){"Non-Compliant"}else{"Unknown"})"
            $Device | Add-Member -MemberType NoteProperty -Name "Name" -Value "$($Target.MachineName)"
            $Device | Add-Member -MemberType NoteProperty -Name "State" -Value "$(if ($Target.AppStatusType -eq 1) {"Success"} elseif ($Target.AppStatusType -eq 2) {"In Progress"} elseif ($Target.AppStatusType -eq 3){"Requirements Not Met"} elseif ($Target.AppStatusType -eq 4) {"Unknown"} elseif($target.AppStatusType -eq 5) {"Error"})"
            #If($Device.State -eq "
                   
            $Device | Add-Member -MemberType NoteProperty -Name "ErrorCode" -Value "$ErrorCode"
            $Device | Add-Member -MemberType NoteProperty -Name "ErrorMessage" -Value "$ErrorMessage"
        
            Try {

            $CIComplianceInfo = $CIComplianceInfo | Sort-Object -Property ComplianceStartDate -Descending | Select * -First 1
        
            $Device | Add-Member -MemberType NoteProperty -Name "LastComplianceStateChange" -Value "$([Management.ManagementDateTimeConverter]::toDateTime($($CIComplianceInfo.ComplianceStartDate)))"
            }
            Catch {
              $Device | Add-Member -MemberType NoteProperty -Name "LastComplianceStateChange" -Value ""
              }

        
            $Devices += $Device


        }

        $OutputReport | Add-Member -MemberType NoteProperty -Name "DetailTitle" -Value $DetailTitle

         $OutputReport | Add-Member -MemberType NoteProperty -Name "DetailResults" -Value $Devices

    }
     
     
     If ($OutputType -eq "Object") {
          Write-Output $OutputReport
    }
    elseif ($OutputType -eq "Text") {
        
        If ($DetailLevel -eq "Summary" -or $DetailLevel -eq "Both"){
            Write-Host ""
            $OutputReport.SummaryTitle
            Write-Host ""
            $($OutputReport.SummaryResults | ft -AutoSize)

        }

        If ($DetailLevel -eq "Detail" -or $DetailLevel -eq "Both"){

            Write-Host ""
            $OutputReport.DetailTitle
            Write-Host ""
            $($OutputReport.DetailResults | Select Name, State, ErrorCode, ErrorMessage, LastComplianceStateChange -Unique | Sort-Object -Property Name | ft -AutoSize)
        }
    }


}


 

 

 

Article By Steve Bowman
Steve Bowman is a Partner at Model Technology as well as their Vice President of Sales and Marketing. Steve is a father, husband, Franciscan, and lover of technology. He's bilingual in business and technology and have over 30 years of experience in selling enterprise technology solutions in a variety of industries.

Related Posts

No more cyber insurance forms!

Tired of filling out endless cyber insurance forms?

  • Download accurate security reports about your environment with a single click.
  • Avoid costly reporting errors.
  • Use your time in better ways.
  • Prove your cyber readiness with confidence.

Download our vCISO platform brochure to learn more.