Jump to content
Kev

Use Microsoft Flow to send e-mail notifications of ConfigMgr activities

Recommended Posts

Posted

15-3.png

 

Have you ever wished you automatically received an e-mail every time something specific happens in your ConfigMgr hierarchy, but you couldn’t find an alert or other notification option in the console?  One way that many admins have accomplished this in the past is using a Status Filter Rule to run a PowerShell script which sends an e-mail to your SMTP server.  This is great, but what if you want to do it expand the dynamics a bit and take other actions besides just sending an e-mail?

 

If you haven’t yet had the chance to play with Microsoft Flow, I highly recommend checking it out.  Another equivalent is called Logic Apps which are, for the most part, the same, however Flow is more geared to Office workers, business users, and Sharepoint admins, while Logic Apps has more advanced integration options available.  At the end of the day, both will work for this purpose, so it’s ultimately up to you which you use, and if you know how to use one, you pretty much know how to use the other.   Click here if you want to read more comparisons of the two. You get Flow free with Office 365, but if you want to hit Flow hard and plenty, you may eventually need a fancier slightly less than free version 🙂

 

First, browse to http://flow.microsoft.com

 

Click My Flows, New, Automated from Blank.

 

Enter a name for your flow in the Flow name blank.

1.png

 

Give your new Flow a meaningful name

 

Click Skip (you don’t need to select a trigger yet)

 

In the search blank, enter Request and click on the Request trigger in the top window, and When a HTTP request is received in the Trigger window.

 

2.png

 

In the Request Body JSON blank, enter your JSON code. In this example, we’re sending an e-mail, so we really just need a TO, a SUBJECT, and an e-mail BODY.

 

3.png

4.png

 

Click Show advanced options

5.png

 

Change the method to POST.

 

Click New step to add a new step to your flow.

 

Click on the Office 365 for Outlook category, and find the Send an e-mail action.

 

Add each Dynamic content step to the appropriate fields in the Send an email step.  If you don’t see all of them on the right side, look for a blue clickable See more link in the Dynamic content window to find them.

 

6.png

 

Optionally, you can also click Show advanced options and change the importance of the e-mail and other settings.

 

Click Save to save the two flow steps we created.

 

Now if you look back at the very first step for the HTTP request, you’ll notice a URL is now provided, for example “https://prod-48.westus.logic.azure.com:443/workflows/abcdef/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=abcdef_ghijkl.” 

 

This is a Rest API URL auto-generated by Flow and unique to only this Flow that you’re creating. So keep your URL safe, be sure not to share it (unless you like surprises) and if you create another Flow, realize you won’t use the same one you’re using now :). Save your unique URL off somewhere safe for now. 

 

Now, you have two options for testing out the flow.  You can put together a quick PowerShell script, or use a tool like Postman.

 

Option 1: PowerShell

$uriFlow = "https://prod-109.westus.logic.azure.com:443/workflows/blahblah"
$body = @{
"subject"="Test"
"body"="This is a test from Flow HTTP request"
"to"="you@yourdomain.com"
} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri $uriFlow -Body $body -ContentType "application/json"

Postman method

Launch Postman, click New and Request

 

Click Body and change the radio button to raw and pick JSON (application/json).

 

Type up the JSON code as seen in the example below, using your To, subject, and body.

7.png

 

Click the Send button, and you should receive an e-mail at the To address you used looking a bit like this:

 

8.png

 

If all is well at this point, the only thing left to do is create some PowerShell code that sends the information you want to receive in the e-mail.

 

What are some good examples?

 

One possibility is, you may want to be notified when things happen in ConfigMgr. For example, every time your Software Update Point synchronizes with Microsoft and your Automatic Deployment Rule (ADR) adds updates to a Software Update Group and deploys them, you might want to know exactly what was deployed without having to go look in the console each time.

 

So, the first step would be to figure out what Status Message ID you want.  In my example, Message ID 5800 is generated each time my ADR runs and adds software updates to a Software Update Group and deploys them.  However, when an administrator deploys baselines, this also generates the same 5800 Message ID, so I need to make sure I’m selective in my PowerShell script in order to filter these other CI Assignments out.

 

9.png

 

Also, if you open the status message, you can decide how to customize your script based on the information you want within the status message. 

 

For example, Message ID 5800 mentions the Software Update Group which was deployed.  This is useful because you can use the Get-CMSoftwareUpdateGroup CmdLet to find out what updates are in a group. 

 

I’ll use my ADR rule named OS Updates in this example. 

10.png

 

Here’s what the status message looks like when it runs and creates the Software Update Group:

11.png

 

In your PowerShell script, the first thing you always need to do whenever utilizing ConfigMgr PowerShell cmdlets, is import the ConfigMgr PowerShell module.  There are some different ways to accomplish this, and you could also dynamically pass the site code to the command-line of the PowerShell script and add a parameter to the PARAM section of the script to dynamically capture the site code as needed if you had more than one site doing this.  For simplicity, I’m going to just use a single stand-alone Primary and hardcode the site code.

 

Once we’ve picked the Status Message we want to trigger our Flow on, click Status Filter Rules to create a new one.

12.png

 

Name it something useful, and enter the Message ID (5800 in my example):

13.png

 

Click the Actions tab, and select the Run a program checkbox.  This is where we need to enter our command-line to launch our PowerShell script.

 

In my example, I will use the following command-line:

C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -nologo -noprofile -executionpolicy bypass -file c:\scripts\Get-UpdateGroupContent.ps1 -Desc "%msgdesc"

 

This assumes my script is sitting in c:\scripts and named Get-UpdateGroupContent.ps1.  %msgdesc is the magic variable that corelates to the Description field in the status message.

14.png

 

Next, move the Status Filter rule up in priority so it is somewhere higher in priority than the canned rules 14 and 15.

 

Within the script, I needed to split up the Description value of %msgdesc to collect ONLY the information I wanted to take our next action on. In this example, if I split every word into an array with the Powershell .Split, and look at array members 8 and 9 and -join them back together, i’m left with only the Software Update Group name MINUS the date and time automatically appended to it by ConfigMgr.  Set it to a variable like $DeploymentName.

 

So I needed to scrape just the Description field out of the Status Message: CI Assignment Manager successfully processed new CI Assignment OS Updates 2019-06-26 22:01:13.

 

And set a variable named $DeploymentName to equal OS Updates which is the actual name of my ADR.

 

$Split = $Desc.Split(" ")
$DeploymentName=($Split[8], $Split[9] -join " ")

 

Now $DeploymentName = OS Updates, the name of my ADR.

 

Next, I’ll check to see if the result of a Get-CMSoftwareUpdateGroup using the -Name of my $DeploymentName variable is $null or not (simply putting code into the () of an IF statement equates to “If the results of this are not NULL”)

If ($SUG = Get-CMSoftwareUpdateGroup -Name "$($DeploymentName)*" | Where-Object {(Get-Date $_.DateCreated -Format D) -eq (Get-Date -Format D)})

I also don’t want to get an e-mail every time my SCEP/Defender definitions are updated, so I add a line to filter it out as well:

If ($DeploymentName -notlike "*Definition*")

 

Next, I will build a PowerShell hashtable using a PSObject and add values to its collection and set all of the variables returned by the Get-CMSoftwareUpdate using the CI_ID  information to a variable called $info.  You can choose any properties from the results you like.  I decided the name of the update, the URL, the date it was released, and the maximum runtime setting would be most helpful.

$info = @()
ForEach ($item1 in (Get-CMSoftwareUpdate -UpdateGroupID $SUG.CI_ID))
     {
     $object = New-Object -TypeName PSObject
     $object | Add-Member -MemberType NoteProperty -Name "Update Title" -Value $item1.LocalizedDisplayName
     $object | Add-Member -MemberType NoteProperty -Name URL -Value $item1.LocalizedInformativeURL
     $object | Add-Member -MemberType NoteProperty -Name "Date Posted" -Value $item1.DatePosted
     $object | Add-Member -MemberType NoteProperty -Name "Max RunTime (mins)" -Value $item1.MaxExecutionTime
     $info += $object
     }

I also decided I want to apply a little CSS to beautify my e-mails a bit:

$head = @"
 <Title>Software Updates Group Report</Title>
<style>
body { background-color:white;
       font-family:Arial;
       font-size:10pt; 
       column-width:150px;}
td, th { border:1px solid black; 
         border-collapse:collapse;
         column-width:150px;
          }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ; }
tr:nth-child(odd) {background-color: grey}
table { width:95%;margin-left:0px; margin-bottom:20px;}
h2 {
 font-family:Tahoma;
 color:#6D7B8D;
}
</style>
"@

$html = $info | ConvertTo-html -Fragment | out-string

 

So when it’s all done and your ADR runs, here’s what you get!

15.png

 

What’s another useful way to use this capability? What about sending all administrative audit status messages up to a Sharepoint list or SQL database or Excel spreadsheet?  The possibilities are endless!

image016.png

 

And finally, here’s the full script. Don’t forget to replace the $SiteCode and $uri variables to match your environment.

[CmdletBinding()]

param(
    # Software Update Group
    [Parameter(Mandatory = $true, ValueFromPipeline=$true)]
    [String] $Desc
    )

$SiteCode = "PRI"

# Load ConfigMgr module if it isn't loaded already
if (-not(Get-Module -name ConfigurationManager)) 
   {
        Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
   }

# Change to site
    Push-Location
    Set-Location ${SiteCode}:

    $Split = $Desc.Split(" ")
    $DeploymentName=($Split[8], $Split[9] -join " ")

If ($SUG = Get-CMSoftwareUpdateGroup -Name "$($Deploymentname)*" | Where-Object {(Get-Date $_.DateCreated -Format D) -eq (Get-Date -Format D)})
{

If ($DeploymentName -notlike "*Definition*")

    {

$info = @()

    ForEach ($item1 in (Get-CMSoftwareUpdate -UpdateGroupID $SUG.CI_ID))
         {
         $object = New-Object -TypeName PSObject
         $object | Add-Member -MemberType NoteProperty -Name "Update Title" -Value $item1.LocalizedDisplayName
         $object | Add-Member -MemberType NoteProperty -Name URL -Value $item1.LocalizedInformativeURL
         $object | Add-Member -MemberType NoteProperty -Name "Date Posted" -Value $item1.DatePosted
         $object | Add-Member -MemberType NoteProperty -Name "Max RunTime (mins)" -Value $item1.MaxExecutionTime
         $info += $object
         }


$head = @"
 <Title>Software Updates Group Report</Title>
<style>
body { background-color:white;
       font-family:Arial;
       font-size:10pt; 
       column-width:150px;}
td, th { border:1px solid black; 
         border-collapse:collapse;
         column-width:150px;
          }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ; }
tr:nth-child(odd) {background-color: grey}
table { width:95%;margin-left:0px; margin-bottom:20px;}
h2 {
 font-family:Tahoma;
 color:#6D7B8D;
}
</style>
"@

$html = $info | ConvertTo-html -Fragment | out-string

$Title = "Total assigned software updates in " + $item.LocalizedDisplayName + " = " + $info.count

# Required API Variables
$uri = 'https://prod-109.westus.logic.azure.com:443/workflows/abcdefg7b8b34e76a2b85a3c517fa5b4/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=abcdef5Ap7w1fEr0cMk5Z7yuo5F_JYuRabcdefg'
$body = @{
     "to"= "russ@russrimmerman.com";
     "subject"="Software Update Group Report"
     "body"=$head + $html
}
#$body
# RESTful API Call
$r = Invoke-WebRequest -Uri $uri -Method Post -ContentType "application/json" -Body (Convertto-Json -InputObject $Body)
    }
}

Source russthepfe.com

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...