Every month when patches come out I have the annoying task of gathering up all of the patches that we will be releasing and sending out an e-mail with the list of all of the patches. Well, while I haven’t quite fully removed the need for me to do that yet I certainly found a way to make the process much more convenient for initial review by the ConfigMgr Engineering team by sending all of the new patches pulled into our ADR’s to Microsoft Teams. This is a great way to get a first glance at what we are going to deploy in the coming days.

First we establish our workflow, WSUS Syncs, our ADR’s run, the results are compiled into the monthly Pilot SUG and that SUG is then polled for membership and the result sent to teams. The below function can be used to get the membership of any SUG in your environment and then send that list of updates to teams!

In order to do this we’ve got three basic requirements.

First we need to get the information out of the SUG we can do this a couple of different ways I think the simplest way is to use the ConfigMgr Cmdlet library, even if getting the information from SQL is faster and more efficient.

So within the ConfigMgr PowerShell cmdlet library we have a few built in cmdlets that one would think would make this entire process really simple however there are a few ‘gotcha’ items to watch out for.

Get-CMSoftwareUpdateGroup -Name $SUG | Get-CMSoftwareUpdate -UpdateGroupID $_.CI_ID

The above seems like a normal line of PowerShell, should simply get the CM-SoftwareUpdateGroup and pass the value of $_.CI_ID through to CM-SoftwareUpdate which would return all update objects with a groupID that matches your update group. I’m not sure why at this point but when you attempt to pipe values to the cmdlet CM-SoftwareUpdate the ‘UpdateGroupID’ parameter won’t accept a piped value if you try you get a cannot bind argument to parameter error.

That means we’ve got two ways to attack this, we can either break it apart to no longer be a one liner, or we can make things a little more complicated and a little less efficient and still get a one liner solution. While I typically try to use one liners where possible I think this is a great example where using a function and breaking something apart into multiple lines is better. If you want to do this as a one liner you can its just less efficient and can be accomplished with a simple for each loop.

Get-CMSoftwareUpdateGroup -Name $SUG | select -ExpandProperty updates | ForEach-Object{Get-CMSoftwareUpdate -Id $_ -fast | select ArticleID}

While the above works its not very efficient as it’s re-running the command to get the software update object every single time through the loop so we can attack this two ways we can still do a ‘one liner’ or we can make a function if we did the one liner approach it would look something like this.

$updateInfo = Get-CMSoftwareUpdateGroup -Name $SUG; Get-CMSoftwareUpdate -UpdateGroupID $updateInfo.CI_ID -fast | Select-Object -First 5 ArticleID , LocalizedInformativeURL , LocalizedDisplayName

It’s much faster than iterating through and getting the full objects and of course we could then turn this into a function that returns the above information pretty simply with just a few more bonus lines. Now when I say this is more efficient to break it apart into two separate pieces I mean the difference between 16 seconds and 127 seconds doesn’t seem like much but its almost eight times better and could be the difference for exceeding run time in some cases and its cheaper if you pay for processing time.

These are the building blocks to send a message about what updates you’re going to release to your environment. With a little more nudging about we end up with a couple of functions the first one gets a list of the updates from the SUG of your choice and the second then sends those results to Microsoft Teams via our friend the webhook.

function Get-CMUpdatesinGroup
{
        [CMdletbinding()]
    Param
    (
        [Parameter(Mandatory = $true)]
        [string]$SUGName
    )
    if(Test-ConfigMgrAvailable)
    {
        try
        {
            write-verbose "The ConfigMgr Cmdlet Library is present retrieving CI_ID of SUG"
            $SugInfo = Get-CMSoftwareUpdateGroup -Name $SUGName -verbose:$false | Select-Object -ExpandProperty CI_ID
            write-verbose "Retrieved the CI_ID of the SUG $SugName the CI_ID is $SugInfo"
            write-verbose "Attempting to retreive update information"
            $UpdateInfo = Get-CMSoftwareUpdate -UpdateGroupID $SugInfo -fast -verbose:$false | Select-Object articleID,LocalizedInformativeURL,LocalizedDisplayName
            return $UpdateInfo
        }
        catch
        {
            $errorMessage = $_.Exception.Message
            write-error -Exception CMPatching -Message $errorMessage
        }
    }
}

Then we can send the information returned to teams this function is a little messy as I originally intended to keep it multi-purpose but as I dug more and more into it I found the only good way to really present the data was using a hash. I may come back at a later date and bundle these two functions together and simply make the send message to teams an optional component of a larger function.

function Send-TeamsMessage 
{    
    [CMdletbinding()]
    Param
    (
        [Parameter(Mandatory = $true)]
        [string]$url,
        [Parameter(Mandatory = $false)]
        [array]$UpdateList
    )
    write-verbose "Generating HASH table with list of updates" 
    $HashArray = @()
    #Create Hash Array for presentation of information. 
    Foreach ($Update in $UpdateList)
    {
        $Hash = @{
            Name = ''
            Value = ''
        }
        #Create the Hash Key and values
        $Value = $Update.LocalizedDisplayname
        #Assign the value to be the localized display name or title of the update.
        $Hash.Name = $Update.ArticleID
        #makes the the .NAME field become the article ID in the hash table.
        $Hash.Value = $Value
        #adds the value (Display name to the row in the hash)
        #Add hash to hash array
        $HashArray += $Hash
    }
    write-verbose "Completed creation of HASH now converting to JSON and sending to teams."
    $body = ConvertTo-Json -Depth 4 @{
        title    = "The following updates are slated for deployment"
        #Modify as desired for the Title of the message
        text   = "Please review the below updates"
        #Subsection Message modify as desired for the subtitle of the message.
        sections = @(
          @{
            facts = $HashArray
            #post the hash array value as the facts of the update for the section.
          }
        )
      }
      $Results = Invoke-RestMethod -uri $url -Method Post -body $body -ContentType 'application/json' -ErrorAction Stop
      #Capture the response from the webURL if 1 then succesfully sent.
      if($Results -eq 1)
      {
          write-verbose "Succesfully send list of updates to teams"
      }
      if($Results -ne 1)
      {
          write-verbose "The message was not sent"
      }
}

In the end we get the following output in PowerShell

And then something that looks like this in Teams.

 

I hope you enjoyed this! If you want all of the code in one spot, its a part of the CMUpdateManagement Module in the SCConfigMgr Github as usual I expect this module to grow and change as I add things to it around managing updates.

https://github.com/SCConfigMgr/ConfigMgr/tree/master/Modules/CMUpdateManagement

(2105)

Jordan Benzing

Jordan has been working in the Industry since 2009. Since starting he’s worked with Active Directory, Group Policy, SCCM, SCOM and PowerShell. Jordan currently works in the healthcare industry as an SCCM Infrastructure Team lead supporting over 150,000 endpoints. Most recently his focus has been in SQL Reporting for SCCM, creation of PowerShell scripts to automate tasks and PowerBI.

comments
  • Jonas
    Posted at 09:48 July 18, 2018
    Jonas
    Reply
    Author

    Hi

    Just tested this, it works nicely however our system locale is Swedish which means the output I get in Teams have some letters missing.

    Is there a way to change the the locale to english for just the script?

    Regards
    Jonas

    • Jordan Benzing
      Posted at 20:50 July 18, 2018
      Jordan Benzing
      Reply
      Author

      So you want to change the locale information for the team prior to sending the message, and then change it back?

      Cheers,

      -J

  • Jonas
    Posted at 08:37 July 19, 2018
    Jonas
    Reply
    Author

    Yes, something like that. Not sure it is possible, and its not a big deal really. But it would make it look a little cleaner.

  • Leave a Reply

    This site uses Akismet to reduce spam. Learn how your comment data is processed.