{
  "version": "Notebook/1.0",
  "items": [
    {
      "type": 1,
      "content": {
        "json": "## Microsoft Teams\n---\n\nThis workbook is dedicated to Microsoft Teams Logs."
      },
      "name": "text - 2"
    },
    {
      "type": 9,
      "content": {
        "version": "KqlParameterItem/1.0",
        "crossComponentResources": [
          "{Subscription}"
        ],
        "parameters": [
          {
            "id": "c59abf67-3610-461e-b263-c90ebaf6e59f",
            "version": "KqlParameterItem/1.0",
            "name": "Subscription",
            "type": 6,
            "isRequired": true,
            "typeSettings": {
              "additionalResourceOptions": [],
              "includeAll": true,
              "showDefault": false
            }
          },
          {
            "id": "99e8ff0c-1981-41b9-ba6c-dec28a2f77c0",
            "version": "KqlParameterItem/1.0",
            "name": "Workspace",
            "type": 5,
            "isRequired": true,
            "query": "where type =~ 'microsoft.operationalinsights/workspaces'\r\n| project id",
            "crossComponentResources": [
              "{Subscription}"
            ],
            "typeSettings": {
              "additionalResourceOptions": [],
              "showDefault": false
            },
            "queryType": 1,
            "resourceType": "microsoft.resourcegraph/resources"
          },
          {
            "id": "9dd762f8-8594-432f-b1dc-9561e0b799c6",
            "version": "KqlParameterItem/1.0",
            "name": "TimeRange",
            "type": 4,
            "isRequired": true,
            "value": {
              "durationMs": 86400000
            },
            "typeSettings": {
              "selectableValues": [
                {
                  "durationMs": 300000
                },
                {
                  "durationMs": 900000
                },
                {
                  "durationMs": 1800000
                },
                {
                  "durationMs": 3600000
                },
                {
                  "durationMs": 14400000
                },
                {
                  "durationMs": 43200000
                },
                {
                  "durationMs": 86400000
                },
                {
                  "durationMs": 172800000
                },
                {
                  "durationMs": 259200000
                },
                {
                  "durationMs": 604800000
                },
                {
                  "durationMs": 1209600000
                },
                {
                  "durationMs": 2419200000
                },
                {
                  "durationMs": 2592000000
                },
                {
                  "durationMs": 5184000000
                },
                {
                  "durationMs": 7776000000
                }
              ],
              "allowCustom": true
            }
          }
        ],
        "style": "pills",
        "queryType": 1,
        "resourceType": "microsoft.resourcegraph/resources"
      },
      "name": "parameters - 1"
    },
    {
      "type": 11,
      "content": {
        "version": "LinkItem/1.0",
        "style": "tabs",
        "links": [
          {
            "id": "2cfb496e-8b26-4c13-b91d-04109ee37efc",
            "cellValue": "selectedTab",
            "linkTarget": "parameter",
            "linkLabel": "Overview",
            "subTarget": "Overview",
            "preText": "",
            "style": "link"
          },
          {
            "id": "691ebec0-768b-4006-92b1-75660bc90398",
            "cellValue": "selectedTab",
            "linkTarget": "parameter",
            "linkLabel": "Admin Activity",
            "subTarget": "Admin Activity",
            "preText": "",
            "style": "link"
          },
          {
            "id": "5f1edb44-1df8-4632-a00b-79a027ae4bcd",
            "cellValue": "selectedTab",
            "linkTarget": "parameter",
            "linkLabel": "External User Activity",
            "subTarget": "External User Activity",
            "style": "link"
          },
          {
            "id": "2658dec2-ed33-4a4d-a27a-c68b92fa6945",
            "cellValue": "selectedTab",
            "linkTarget": "parameter",
            "linkLabel": "Hunting",
            "subTarget": "Hunting",
            "style": "link"
          }
        ]
      },
      "name": "links - 6"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "title": "Overview",
        "items": [
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\n| where OfficeWorkload =~ \"MicrosoftTeams\"\n| summarize count() by  Operation, bin_at(TimeGenerated, 1h, now())\n",
              "size": 0,
              "title": "Operations Over Time",
              "timeContext": {
                "durationMs": 0
              },
              "timeContextFromParameter": "TimeRange",
              "timeBrushParameterName": "TimeBrush",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "timechart"
            },
            "name": "query - 2"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload =~ \"MicrosoftTeams\"\r\n| summarize count() by Operation , bin_at(TimeGenerated, 1h, now())\r\n| summarize makelist(count_), sum(count_) by Operation\r\n| order by sum_count_",
              "size": 0,
              "title": "Types of Operation",
              "timeContext": {
                "durationMs": 0
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "tiles",
              "gridSettings": {
                "filter": true
              },
              "tileSettings": {
                "titleContent": {
                  "columnMatch": "Operation",
                  "formatter": 1
                },
                "subtitleContent": {
                  "columnMatch": "sum_count_",
                  "formatter": 12,
                  "formatOptions": {
                    "palette": "blue"
                  }
                },
                "secondaryContent": {
                  "columnMatch": "list_count_",
                  "formatter": 10,
                  "formatOptions": {
                    "palette": "blue"
                  }
                },
                "showBorder": false
              }
            },
            "name": "query - 3"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload =~ \"MicrosoftTeams\"\r\n| summarize  OperationCount = count() , ['Last Activity Time'] = arg_max(TimeGenerated, *) by UserId, Operation\r\n| project OperationCount,  UserId, Operation, ['Last Activity Time']\r\n| order by OperationCount",
              "size": 0,
              "title": "Last Activity based on User",
              "timeContext": {
                "durationMs": 82800000,
                "endTime": "2022-01-28T17:33:18.000Z"
              },
              "timeContextFromParameter": "TimeBrush",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "gridSettings": {
                "formatters": [
                  {
                    "columnMatch": "UserId",
                    "formatter": 5
                  },
                  {
                    "columnMatch": "OperationCount",
                    "formatter": 8,
                    "formatOptions": {
                      "palette": "blue"
                    }
                  }
                ],
                "rowLimit": 10000,
                "hierarchySettings": {
                  "treeType": 1,
                  "groupBy": [
                    "UserId"
                  ],
                  "expandTopLevel": true
                }
              }
            },
            "name": "query - 4"
          }
        ],
        "exportParameters": true
      },
      "conditionalVisibility": {
        "parameterName": "selectedTab",
        "comparison": "isEqualTo",
        "value": "Overview"
      },
      "name": "tabOverview"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "title": "Admin Activity",
        "items": [
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where Operation contains \"Added\" or Operation contains \"Removed\" or Operation contains \"Deleted\" or Operation contains \"Created\"\r\n| summarize count() by Operation",
              "size": 0,
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "crossComponentResources": [
                "{Workspace}"
              ],
              "visualization": "piechart"
            },
            "customWidth": "30",
            "name": "query - 0"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where Operation contains \"Added\" or Operation contains \"Removed\" or Operation contains \"Deleted\" or Operation contains \"Created\"\r\n| project TimeGenerated, Operation, TeamName, ChannelName, UserId\r\n| sort by TimeGenerated desc",
              "size": 0,
              "timeContext": {
                "durationMs": 86400000
              },
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "crossComponentResources": [
                "{Workspace}"
              ]
            },
            "customWidth": "70",
            "name": "query - 1"
          }
        ],
        "exportParameters": true
      },
      "conditionalVisibility": {
        "parameterName": "selectedTab",
        "comparison": "isEqualTo",
        "value": "Admin Activity"
      },
      "name": "Admin Activity"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "items": [
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where CommunicationType == \"Team\"\r\n| where Operation == \"MemberAdded\"\r\n| extend DisplayName_ = tostring(parse_json(Members)[0].DisplayName) \r\n| extend UPN_ = tostring(parse_json(Members)[0].UPN)\r\n| where UPN_ contains \"#EXT#\"\r\n| project TimeGenerated, [\"Added By\"] = UserId, TeamName, DisplayName_, UPN_\r\n| sort by TimeGenerated desc ",
              "size": 0,
              "title": "External Users Added to Team",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "gridSettings": {
                "filter": true
              }
            },
            "name": "query - 0"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where CommunicationType == \"Team\"\r\n| where Operation == \"MemberRemoved\"\r\n| extend DisplayName_ = tostring(parse_json(Members)[0].DisplayName) \r\n| extend UPN_ = tostring(parse_json(Members)[0].UPN)\r\n| where UPN_ contains \"#EXT#\"\r\n| project TimeGenerated, [\"Removed By\"] = UserId, TeamName, DisplayName_, UPN_\r\n| sort by TimeGenerated desc ",
              "size": 0,
              "title": "External Users Removed from the Team",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "gridSettings": {
                "filter": true
              }
            },
            "name": "query - 0 - Copy"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where UserId contains \"#EXT#\"\r\n| project TimeGenerated, Operation, UserId\r\n| sort by TimeGenerated desc",
              "size": 0,
              "title": "External Users Activity",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "gridSettings": {
                "filter": true
              }
            },
            "name": "query - 0 - Copy - Copy"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where UserId contains \"#EXT#\"\r\n| summarize count() by Operation, bin(TimeGenerated, 1d)",
              "size": 0,
              "title": "External Activity, by Day",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces",
              "visualization": "timechart",
              "gridSettings": {
                "filter": true
              }
            },
            "name": "query - 0 - Copy - Copy - Copy"
          }
        ]
      },
      "conditionalVisibility": {
        "parameterName": "selectedTab",
        "comparison": "isEqualTo",
        "value": "External User Activity"
      },
      "name": "External User Activity"
    },
    {
      "type": 12,
      "content": {
        "version": "NotebookGroup/1.0",
        "groupType": "editable",
        "items": [
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "// If you want to look at user added further than 7 days ago adjust this value \r\nlet time_ago = 7d; \r\n// If you want to change the timeframe of how quickly accounts need to be added and removed change this value \r\nlet time_delta = 1h; \r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where TimeGenerated > ago(time_ago) \r\n| where Operation =~ \"MemberAdded\" \r\n| extend UPN = tostring(parse_json(Members)[0].UPN) \r\n| where UPN contains \"#EXT#\"\r\n| project TimeAdded=TimeGenerated, Operation, UPN, UserWhoAdded = UserId, TeamName, TeamGuid\r\n| join ( \r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where TimeGenerated > ago(time_ago) \r\n| where Operation =~ \"MemberRemoved\" \r\n| extend UPN = tostring(parse_json(Members)[0].UPN) \r\n| where UPN contains \"#EXT#\"\r\n| project TimeDeleted=TimeGenerated, Operation, UPN, UserWhoDeleted = UserId, TeamName, TeamGuid) on UPN, TeamGuid \r\n| where TimeDeleted < (TimeAdded + time_delta) \r\n| project TimeAdded, TimeDeleted, UPN, UserWhoAdded, UserWhoDeleted, TeamName, TeamGuid \r\n// Uncomment the following line to map query entities is you plan to use this as a detection query \r\n| extend timestamp = TimeAdded, AccountCustomEntity = UPN \r\n",
              "size": 0,
              "title": "External Users Added and Removed",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 0"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "// If you have more than 14 days worth of Teams data change this value \r\nlet data_date = 14d; \r\n// If you want to look at users further back than the last day change this value \r\nlet lookback_data = 1d; \r\nlet known_orgs = ( \r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where TimeGenerated > ago(data_date) \r\n| where Operation =~ \"MemberAdded\" or Operation =~ \"TeamsSessionStarted\" \r\n// Extract the correct UPN and parse our external organization domain \r\n| extend UPN = iif(Operation == \"MemberAdded\", tostring(parse_json(Members)[0].UPN), UserId) \r\n| extend Organization = tostring(split(split(UPN, \"_\")[1], \"#\")[0]) \r\n| where isnotempty(Organization) \r\n| summarize by Organization); \r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where TimeGenerated > ago(lookback_data) \r\n| where Operation =~ \"MemberAdded\" \r\n| extend UPN = tostring(parse_json(Members)[0].UPN) \r\n| extend Organization = tostring(split(split(UPN, \"_\")[1], \"#\")[0]) \r\n| where isnotempty(Organization) \r\n| where Organization !in (known_orgs) \r\n// Uncomment the following line to map query entities is you plan to use this as a detection query \r\n| extend timestamp = TimeGenerated, AccountCustomEntity = UPN \r\n",
              "size": 0,
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 1"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": " // Adjust this value to change how many Teams should be deleted before including\r\n let max_delete = 3;\r\n // Adjust this value to change the timewindow the query runs over\r\n let time_window = 1d;\r\n let deleting_users = (\r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n | where TimeGenerated > ago(time_window)\r\n | where Operation =~ \"TeamDeleted\"\r\n | summarize count() by UserId\r\n | where count_ > max_delete\r\n | project UserId);\r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n | where TimeGenerated > ago(time_window)\r\n | where Operation =~ \"TeamDeleted\"\r\n | where UserId in (deleting_users)\r\n | project-away Members\r\n // Uncomment the following line to map query entities is you plan to use this as a detection query\r\n| extend timestamp = TimeGenerated, AccountCustomEntity = UserId",
              "size": 0,
              "title": "Multiple Teams Deleted by a single user",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 2"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let timeFrame = 1d;\r\nlet logonDiff = 10m;\r\nSigninLogs \r\n  | where TimeGenerated >= ago(timeFrame) \r\n  | where ResultType == \"0\" \r\n  | where AppDisplayName startswith \"Microsoft Teams\"\r\n  | project SuccessLogonTime = TimeGenerated, UserPrincipalName, SuccessIPAddress = IPAddress, AppDisplayName, SuccessIPBlock = strcat(split(IPAddress, \".\")[0], \".\", split(IPAddress, \".\")[1])\r\n  | join kind= inner (\r\n      SigninLogs \r\n      | where TimeGenerated >= ago(timeFrame) \r\n      | where ResultType !in (\"0\", \"50140\") \r\n      | where ResultDescription !~ \"Other\"  \r\n      | where AppDisplayName startswith \"Microsoft Teams\"\r\n      | project FailedLogonTime = TimeGenerated, UserPrincipalName, FailedIPAddress = IPAddress, AppDisplayName, ResultType, ResultDescription\r\n  ) on UserPrincipalName, AppDisplayName \r\n  | where SuccessLogonTime < FailedLogonTime and FailedLogonTime - SuccessLogonTime <= logonDiff and FailedIPAddress !startswith SuccessIPBlock\r\n  | summarize FailedLogonTime = max(FailedLogonTime), SuccessLogonTime = max(SuccessLogonTime) by UserPrincipalName, SuccessIPAddress, AppDisplayName, FailedIPAddress, ResultType, ResultDescription \r\n  | extend timestamp = SuccessLogonTime, AccountCustomEntity = UserPrincipalName, IPCustomEntity = SuccessIPAddress",
              "size": 0,
              "title": "Successful logon from IP and failure from a different IP (Teams Signins)",
              "timeContext": {
                "durationMs": 0
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 3"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "// Adjust this value to change how many teams a user is made owner of before detecting \r\nlet max_owner_count = 3; \r\n// Change this value to adjust how larger timeframe the query is run over. \r\nlet time_window = 1d; \r\nlet high_owner_count = (OfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\" \r\n| where TimeGenerated > ago(time_window) \r\n| where Operation =~ \"MemberRoleChanged\" \r\n| extend Member = tostring(parse_json(Members)[0].UPN)  \r\n| extend NewRole = toint(parse_json(Members)[0].Role)  \r\n| where NewRole == 2 \r\n| summarize dcount(TeamName) by Member \r\n| where dcount_TeamName > max_owner_count \r\n| project Member); \r\nOfficeActivity\r\n| where OfficeWorkload == \"MicrosoftTeams\"\r\n| where TimeGenerated > ago(time_window) \r\n| where Operation =~ \"MemberRoleChanged\" \r\n| extend Member = tostring(parse_json(Members)[0].UPN)  \r\n| extend NewRole = toint(parse_json(Members)[0].Role)  \r\n| where NewRole == 2 \r\n| where Member in (high_owner_count)  ",
              "size": 0,
              "title": "User is made owner",
              "timeContext": {
                "durationMs": 0
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 4"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "let suspiciousExtensions = dynamic(['exe', 'msi', 'bin', 'dll']);\r\nOfficeActivity \r\n| where RecordType =~ \"SharePointFileOperation\" \r\n| where UserId !~ \"app@sharepoint\"\r\n| where SourceRelativeUrl has \"Microsoft Teams Chat Files\" \r\n| where Operation =~ \"FileUploaded\" \r\n| project TimeGenerated, UserId, ClientIP, Site_Url, OfficeObjectId, SourceFileName, SourceFileExtension\r\n| where SourceFileExtension has_any(suspiciousExtensions)",
              "size": 0,
              "title": "Sharing - Suspicious FIle Extensions",
              "timeContext": {
                "durationMs": 86400000
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 5"
          },
          {
            "type": 3,
            "content": {
              "version": "KqlItem/1.0",
              "query": "//The bigger the window the better the data sample size, as we use IP prevalence, more sample data is better.\r\n  let timeRange = 30d;\r\n  //The minimum number of countries that the account has been accessed from [default: 2]\r\n  let minimumCountries = 2;\r\n  //The delta (%) between the largest in-use IP and the smallest [default: 90]\r\n  let deltaThreshold = 95;\r\n  //The maximum (%) threshold that the country appears in login data [default: 10]\r\n  let countryPrevalenceThreshold = 10;\r\n  //The time to project forward after the last login activity [default: 60min]\r\n  let projectedEndTime = 60min; \r\n  //Get Teams successful signins globally\r\n  let signinData =\r\n    SigninLogs\r\n    | where TimeGenerated >= ago(timeRange)\r\n    | where AppDisplayName has \"Teams\"\r\n    | where ConditionalAccessStatus =~ \"success\"\r\n    | extend country = tostring(LocationDetails['countryOrRegion'])\r\n    | where isnotempty(country) and isnotempty(IPAddress);\r\n  // Collect successful signins to teams\r\n  let loginEvents = \r\n    signinData\r\n    | summarize count(), country=any(country), make_list(TimeGenerated) by IPAddress, UserPrincipalName;\r\n  //Calcualte delta between logins\r\n  let loginDelta =\r\n    loginEvents\r\n    | summarize max(count_), min(count_) by UserPrincipalName\r\n    | extend delta = toreal(max_count_ - min_count_) / max_count_ * 100\r\n    | where delta >= deltaThreshold;\r\n  //Count number of countries used to sign in\r\n  let countryCount =\r\n    loginEvents\r\n    | summarize Countries = dcount(country) by UserPrincipalName;\r\n  //Join delta and sign in counts to successful logins\r\n  loginDelta\r\n  | join kind=rightouter  (\r\n    loginEvents\r\n  ) on UserPrincipalName\r\n  | join kind=rightouter (\r\n    countryCount\r\n  ) on UserPrincipalName\r\n  //Check where the record meets the minimum required countries\r\n  | where Countries >= minimumCountries\r\n  | join kind=leftouter (\r\n        signinData\r\n        | summarize count() by country\r\n        | join (\r\n            //Now get the total number of logins from any country and join it to the previous count in a single table\r\n            signinData\r\n            | summarize count() by country\r\n            | summarize sum(count_), make_list(country)\r\n            | mv-expand list_country\r\n            | extend country = tostring(list_country)\r\n        ) on country\r\n        | summarize by country, count_, sum_count_\r\n        //Now calculate each countries prevalence within login events\r\n        | extend prevalence = toreal(count_) / toreal(sum_count_) * 100\r\n        | project-away sum_count_\r\n        | order by prevalence\r\n  ) on country\r\n  //The % that suspicious country is prevalent in data, this can be configured, less than 10% is uncommon\r\n  | where prevalence < countryPrevalenceThreshold\r\n  | where min_count_ == count_\r\n  //Login start and end times from the JSON object, this is the activity window the suspicious IP was active within\r\n  | extend EventTimes = list_TimeGenerated\r\n  | extend SuspiciousIP = IPAddress\r\n  | project UserPrincipalName, SuspiciousIP, UserIPDelta = delta, SuspiciousLoginCountry = country, SuspiciousCountryPrevalence = prevalence, EventTimes\r\n  //Teams join to collect operations the user account has performed within the given time range\r\n  | join kind=inner( \r\n    OfficeActivity\r\n    | where OfficeWorkload == \"MicrosoftTeams\"\r\n    | where TimeGenerated >= ago(timeRange)\r\n    | where Operation in~ (\"TeamsAdminAction\", \"MemberAdded\", \"MemberRemoved\", \"MemberRoleChanged\", \"AppInstalled\", \"BotAddedToTeam\")\r\n    | project Operation, UserId=tolower(UserId), OperationTime=TimeGenerated\r\n  ) on $left.UserPrincipalName == $right.UserId\r\n  | mv-expand StartTime = EventTimes\r\n  | extend StartTime = make_datetime(StartTime)\r\n  //The end time is projected 60 minutes forward, in case actions took place within the last hour of the final login for the suspicious IP\r\n  | extend ProjectedEndTime = make_datetime(StartTime + projectedEndTime)\r\n  //Limit to operations carried out by the user account in the timeframe the IP was active\r\n  | where OperationTime between (StartTime .. ProjectedEndTime)\r\n  | project UserPrincipalName, SuspiciousIP, StartTime, ProjectedEndTime, OperationTime, Operation, SuspiciousLoginCountry, SuspiciousCountryPrevalence\r\n  //Filter on suspicious actions\r\n  | extend activitySummary = pack(tostring(StartTime), pack(\"Operation\",tostring(Operation), \"OperationTime\", OperationTime))\r\n  | summarize make_bag(activitySummary) by UserPrincipalName, SuspiciousIP, SuspiciousLoginCountry, SuspiciousCountryPrevalence\r\n  | extend IPCustomEntity = SuspiciousIP, AccountCustomEntity = UserPrincipalName",
              "size": 0,
              "title": "Anomalous login followed by Teams action",
              "timeContext": {
                "durationMs": 0
              },
              "timeContextFromParameter": "TimeRange",
              "queryType": 0,
              "resourceType": "microsoft.operationalinsights/workspaces"
            },
            "name": "query - 6"
          }
        ]
      },
      "name": "Hunting"
    }
  ],
  "fromTemplateId": "sentinel-MicrosoftTeamsWorkbook",
  "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
}
