Accessing Office 365 Security & Compliance Center Logs from Microsoft Flow

In a previous blog post, we discussed Microsoft Flow audit events surfacing in the Office 365 Security & Compliance Center.  We have had inquiries from customers and partners about programmatically accessing this data. Something that is of particular interest, is understanding what connectors are being used in new, or updated, flows.  For some organizations, this information will help them with their Data Loss Prevention (DLP) strategies.  For others, they will use this as a change management opportunity where they can ensure users that are creating flows have been able to achieve their desired business outcomes.

The purpose of this post is to discuss one of the ways that Microsoft Flow audit events can be retrieved using Microsoft Flow.  In addition, to make this process even simpler we have recently published 2 templates to quickly get you started.  

There are multiple ways to access Office 365 Security & Compliance Center audit information programmatically including:

This post is going to focus on using the PowerShell Search-UnifiedAuditLog Webservice as we will cover the other approaches in future blog posts.  One of the benefits of using the PowerShell Webservice is we can make a simple http request and use Microsoft Flow to manage our process!

The scenarios that we are going to discus in this post include an Office 365 Tenant Admin being notified when:

  • A new flow is created within their tenant.
  • An existing flow is edited within their tenant.

Templates have been provisioned for both the Create and Edit scenarios.

Pre-requisites

To call the PowerShell Webservice, you will need to provide the following information:

URL

https://outlook.office365.com/psws/service.svc/UnifiedAuditLog

StartDate

Query parameter - A date to start filtering events i.e. 11/29/2017

EndDate

Query parameter – The end date for events we are interested in i.e. 11/30/2017

Operations

Query parameter – A filter for events we are interested in i.e. CreateFlow

Authorization

Header – We need to provide an Authorization header based on Basic Authentication. Note: To call this API, the authorization token must belong to someone who has Office 365 tenant administration privileges.

Accept

Header – By default the PowerShell Webservice will return Xml.  In our scenario we want JSON so we will pass in application/json.

Before we dive into the Flow template, lets test our connectivity using Postman, a popular tool for testing APIs. If we can successfully call the PowerShell Webservice from Postman, calling it from Microsoft Flow will not be a problem.

  1. In Postman, ensure GET is selected and then provide the URL for the Office 365 API.  In this case, the following was provided: https://outlook.office365.com/psws/service.svc/UnifiedAuditLog?StartDate=11/20/2017&EndDate=11/30/2017&Operations=CreateFlow. Note, the StartDate, EndDate and Operations query parameters which will be used to filter our results.

  1. Next, click on the Authorization tab and then provide a Username and Password followed by clicking on the Update Request button. As noted in the tool, Postman will automatically create the authorization header including our encoded token.

  1. After completing the authorization step, click on the Headers tab and then provide a Key of Accept and a Value of application/json. Click Send to submit your request. Notice, upon clicking the Send button, an Authorization Header is added.

  1. We now have results that include data about any Flows that were created, by whom and when.

 

Get List of New Flows Template

As the heading suggests, this template will retrieve a list of new flows that have been provisioned in a tenant within the past day. Let’s now take a closer look into how this template was built.

  1. In our scenario, we want this flow to run every day.  To enable this, we will use a Recurrence trigger with an Interval of 1 Day.

  1. We want to dynamically generate our StartDate and EndDate query parameters. To accomplish this, we will initialize two variables and use expressions to drive the dynamic values.  For StartDate  we will use an expression of startOfDay(utcNow()) and for EndDate we will use startOfDay(addDays(utcNow(),1,'MM/dd/yyyy')) which will generate the start of the next day.

  1. Next, we will initialize a string variable, called EmailOutput which will be used to construct our email digest content that is sent out at the end of our process.

  1. Another variable that we want to initialize is a FlowDetails array where we can persist results coming back from the PowerShell Webservice.

  1. Now, we will create our HTTP Request for the PowerShell Webservice.  Notice we are passing in our StartDate and EndDate parameters that we previously instantiated.  We are also setting the Operations query parameter to CreateFlow. In addition, we need to provide a Username and Password as part of our Basic authentication call. These values are exposed by clicking on the Show advanced options link.

  1. The output from the PowerShell Webservice call will look like the following

  "odata.metadata": "https://outlook.office365.com/Psws/Service.svc/$metadata#UnifiedAuditLog",
  "value": [
    {
      "Identity": "4cc22367-e785-475c-91a1-32ee45b1",
      "AuditData": "{\"CreationTime\":\"2017-11-30T19:29:48\",\"Id\":\"4cc22367-e785-475c-91a1-32ee45b1b\", \"Operation\":\"CreateFlow\",\"OrganizationId\":\"1607f60f-8e03-40ef-a4b5-7e5cb0\",\"RecordType\":30,\"ResultStatus\":\"Success\",\"UserKey\":\"kentweare@wearsy.onmicrosoft.com\",\"UserType\":0, \"Version\":1,\"Workload\":\"MicrosoftFlow\", \"UserId\":\"kentweare@wearsy.onmicrosoft.com\",\"FlowConnectorNames\":\"Request,Response,Http\", \"FlowDetailsUrl\":\"https:\\/\\/admin.flow.microsoft.com\\/environments\ \/Default-160760f-8e03-40ef-a4b5-7e5cb07b\\/resources\\/flows\\/eebc7c0-ef13-405c-96a7-b27710692\",\"LicenseDisplayName\":\"\",\"RecipientUPN\":\"\",\"SharingPermission\":1,\"UserTypeInitiated\":1,\"UserUPN\":\"kentweare@wearsy.onmicrosoft.com\"}",
      "CreationDate": "2017-11-30T19:29:48Z",
      "IsValid": true,
      "ObjectState": "Unchanged",
      "Operations": "CreateFlow",
      "RecordType": "30",
      "ResultCount": 1,
      "ResultIndex": 1,
      "UserIds": "kentweare@wearsy.onmicrosoft.com",
      "SiteIds": [],
      "ObjectIds": null,
      "IPAddresses": null
    }
  ]
}

We will use this sample data to construct a JSON schema within our Parse Audit Response action.  We need typed data to further use this data in our upcoming email digest. 

  1. Our next step is to see if we have any records returned from our PowerShell Webservice.  We will create an expression with a condition that will check the length of our response from the webservice call: length(body('Parse_Audit_Response')?['value']) and see if the return value is greater than 0.

  1. If there are records, we will parse through each record, or flow details, and retrieve the AuditData node.  Within this node, there is untyped data that we need to apply a schema to so that we can parse out only the attributes that we are interested.

AuditData":"{\"CreationTime\":\"2017-11-30T19:29:48\",\"Id\":\"4cc22367-e785-475c-91a1-32ee45b1b\",\"Operation\":\"CreateFlow\",\"OrganizationId\":\"1607f60f-8e03-40ef-a4b5-7e5cb0\",\"RecordType\":30,\"ResultStatus\":\"Success\",\"UserKey\":\"kentweare@wearsy.onmicrosoft.com\", \"UserType\":0,\"Version\":1,\"Workload\":\"MicrosoftFlow\", \"UserId\":\"kentweare@wearsy.onmicrosoft.com\",\"FlowConnectorNames\":\"Request,Response,Http\", \"FlowDetailsUrl\":\"https:\\/\\/admin.flow.microsoft.com\\/environments\\/Default-1607f60f-8e03-40ef-a4b5-7e5cb07db\\/resources\\/flows\\/eebcc7c0-ef13-405c-96a7-b27710692\", \"LicenseDisplayName\":\"\",\"RecipientUPN\":\"\",\"SharingPermission\":1, \"UserTypeInitiated\":1,\"UserUPN\":\"kentweare@wearsy.onmicrosoft.com\"}”

We can use this sample data as an input to generate our JSON schema. Within this loop we will then take the output from our Parse AuditDetails action and append it to our FlowDetails array.

  1. As you can see from step 8, there are a lot of details being captured within the AuditData node.  We do not need to include all this information in our email digest, so we can reduce the number of attributes that we need by using the Select action. Within the Select action we can create a table that includes user-friendly column names.  We can then map those column names to their corresponding values buy using expressions such as: item()['CreationTime'].

  1. Next, we will take use our FlowDetails array to create an HTML table.

  1. We now have an HTML table, but need to wrap some additional content around it.  We will update our EmailOutput variable to include some additional HTML markup.  Also note that we can include CSS stylesheet content when embedding that markup in <style> tags. As part of this content we will trim our Start and End dates, to remove timestamps, and include the number of flows that have been created by getting the length of our FlowDetails array: length(variables('FlowDetails')). We will also include our HTML table that we created in the previous step.

  1. In the event there are no new flows created in the past day, we will end up in the If no branch.  Within this condition, we will simply update our EmailOutput variable to indicate we have no new flows that have been added to our tenant within our date range. To make our text cleaner, we will exclude the timestamp from our markup.  We can accomplish by using the following expression: substring(variables('StartDate'),0,10).

  1. Lastly, we will send out our email using our EmailOutput variable as our message body.

Testing

Once we have provided our required email address and HTTP Authorization details, we can save our flow and it will execute.

In the following example, 1 new flow was added to the tenant.  We can determine the following:

  • The Create Datetime of the flow.
  • The User Id who created it.
  • Whether the flow was created successfully.
  • The connectors used in the flow.
  • A link to Flow Admin center where more information is available about the flow.
  • The current sharing permission value.
    • None = 1,
    • ReadOnly = 2,
    • ReadWrite = 3

Getting Notifications of Updated Flows

We have also deployed a template that will look for any flows that have been updated in the past day.  The flow is very similar as the Create Flow template, with the most significant change being the URL that the HTTP request is sent to. We will pass in an Operations parameter of EditFlow to get a list of flows that have been edited.

https://outlook.office365.com/psws/service.svc/UnifiedAuditLog?StartDate=2017-11-30&EndDate=2017-12-01&Operations=EditFlow.

When we run this flow, the output is very similar except the content focuses on flows that have been edited instead of created.

Other Operations

In this blog post we covered two of the seven audit events that are currently available in the Office 365 Security & Compliance Center.  The other five audit events are also available for retrieval by passing in the following values in the Operations query parameter.  Here is a complete list of the operations that can be called:

Operation Name

Description of Event

CreateFlow

When a new flow is created.

EditFlow

When an existing flow is modified.

DeleteFlow

When a flow is deleted.

PutPermissions

When permissions are modified on a flow.

StartAPaidTrial

When a user begins a paid trial.

RenewTrial

When a user renews a paid trial.

Basic Authentication

In the templates that have been published, we are using Basic Authentication as our authentication mechanism.  While flow configuration and run-time data is encrypted at rest and in-transit, some organizations may be reluctant to store credentials within the flow. There are a few options to deal with this:

  1. Use a Button trigger, from web browser or mobile application, to kick off the flow and include information that can be used to construct the Authorization header by calling an Azure Function.  Once encoded, this token can be passed as part of the HTTP Request. Authorization headers are never visible in Flow Run Details as inputs or outputs as shown below.

  1. Use either the Office 365 Management Activity API or PowerShell Search-UnifiedAuditLog cmdlet.  Both of these approaches require an interactive session to be established before any data can be retrieved.  This makes automating the process a little more challenging, but neither of these approaches are dependent upon Basic Authentication.  We will cover both of these topics in upcoming blog posts so stay tuned.

Conclusion

In this blog post we explored how Office 365 Tenant admins can programmatically retrieve Microsoft Flow audit data.  Once you have the data, the possibilities are endless with what you can do with it.  You can create Power BI dashboards and share it with interested stakeholders.  You can also publish this data to Security Information and Event Management (SIEM) systems like Splunk.

We are continuing to invest in our Flow Admin capabilities and these templates are another tool available to apply governance processes and create opportunities for your business. If you have ideas for Admin related templates, analytics or other feedback, please leave us comments below or post on our Community forum.