Penfield and Microsoft Sentinel integration (Logic App)
Overview
This document outlines the process of integrating Microsoft Sentinel with the Penfield Platform. The integration involves two main steps:
- Building Azure Logic App Playbooks to push relevant Incident Data to Penfield Platform via Penfield's API.
- Creating Automation Rules in Sentinel to trigger specific playbooks when certain incident conditions are met.
Prerequisites
- Access to Microsoft Azure portal
- Permissions to create and manage Logic Apps and Microsoft Sentinel
- Penfield API endpoint and API key (provided by Penfield)
Step 1: Building Azure Logic App Playbooks
1.1 Create a Logic App
- In the Azure portal, search for "Logic Apps”
- If you already have a Logic App built for Sentinel, you may choose to utilize that instead and jump to 1.2.
- Click "Create" to create a new Logic App
- Fill in the required details:
- Resource Group: Select or create a new one
- Logic App name: e.g., "Penfield-Sentinel-Integration"
- Region: Choose appropriate region
- Plan type: Consumption
- Click "Review + Create" and then "Create"
1.2 Create Workflows
Create three workflows within the Logic App:
Create 3 Workflows: Next we need to create 3 workflows.
Workflow 1: create-incident
Select Add and fill in Workflow Name:
Select Designer
Select Add Trigger
Search for “sentinel Incident” and select “Microsoft Sentinel incident” fom the list.
Select (+) under Microsoft Sentinel incident and then search for “HTTP” and select
- Configure the HTTP action:
- Method: POST
- URI: [Provided by Penfield]
- Headers:
- Content-Type: application/json
- x-api-key: [Provided by Penfield]
- Body: see below
- Body:
{
"type": "create",
"incident": {
"severity": "@{coalesce(triggerBody()?['object']?['properties']?['severity'], 'Unknown')}",
"description": "@{coalesce(replace(coalesce(triggerBody()?['object']?['properties']?['description'], ''), '\\\"', '\\\\\\\"'), 'No description provided')}",
"incident_number": "@{coalesce(triggerBody()?['object']?['properties']?['incidentNumber'], 'Unknown')}",
"incident_title": "@{coalesce(triggerBody()?['object']?['properties']?['title'], 'Untitled Incident')}",
"incident_id": "@{coalesce(last(split(triggerBody()?['object']?['id'], '/')), 'Unknown')}",
"incident_status": "@{coalesce(triggerBody()?['object']?['properties']?['status'], 'Unknown')}",
"assignee": "@{coalesce(triggerBody()?['object']?['properties']?['owner']?['userPrincipalName'], 'Unassigned')}",
"updated_at": "@{coalesce(triggerBody()?['object']?['properties']?['lastModifiedTimeUtc'], utcNow())}",
"tactics": "@{if(empty(triggerBody()?['object']?['properties']?['tactics']), '[]', string(triggerBody()?['object']?['properties']?['tactics']))}",
"classification": "@{coalesce(triggerBody()?['object']?['properties']?['classification'], 'Unclassified')}",
"classification_reason": "@{coalesce(triggerBody()?['object']?['properties']?['classificationReason'], 'Not specified')}",
"classification_comment": "@{coalesce(triggerBody()?['object']?['properties']?['classificationComment'], '')}",
"event_time": "@{utcNow()}"
}
}
Note: Please contact Penfield team should you have privacy concerns. The Penfield platform can be configured to anonymize/pseudonymize specific fields before they are saved. Alternatively, you may choose not to push specific fields to Penfield - the control is yours but please consult Penfield.
End state should look something like this:
Select save.
Workflow 2: assign-incident
Follow the same steps as Workflow 1, but use the following JSON structure in the HTTP action body.
{
"type": "assign",
"incident": {
"severity": "@{coalesce(triggerBody()?['object']?['properties']?['severity'], 'Unknown')}",
"description": "@{coalesce(replace(coalesce(triggerBody()?['object']?['properties']?['description'], ''), '\\\"', '\\\\\\\"'), 'No description provided')}",
"incident_number": "@{coalesce(triggerBody()?['object']?['properties']?['incidentNumber'], 'Unknown')}",
"incident_title": "@{coalesce(triggerBody()?['object']?['properties']?['title'], 'Untitled Incident')}",
"incident_id": "@{coalesce(last(split(triggerBody()?['object']?['id'], '/')), 'Unknown')}",
"incident_status": "@{coalesce(triggerBody()?['object']?['properties']?['status'], 'Unknown')}",
"assignee": "@{coalesce(triggerBody()?['object']?['properties']?['owner']?['userPrincipalName'], 'Unassigned')}",
"updated_at": "@{coalesce(triggerBody()?['object']?['properties']?['lastModifiedTimeUtc'], utcNow())}",
"tactics": "@{if(empty(triggerBody()?['object']?['properties']?['tactics']), '[]', string(triggerBody()?['object']?['properties']?['tactics']))}",
"classification": "@{coalesce(triggerBody()?['object']?['properties']?['classification'], 'Unclassified')}",
"classification_reason": "@{coalesce(triggerBody()?['object']?['properties']?['classificationReason'], 'Not specified')}",
"classification_comment": "@{coalesce(triggerBody()?['object']?['properties']?['classificationComment'], '')}",
"event_time": "@{utcNow()}"
}
}
Workflow 3: resolve-incident
Follow the same steps as Workflow 1, but use the following JSON structure in the HTTP action body.
{
"type": "resolve",
"incident": {
"severity": "@{coalesce(triggerBody()?['object']?['properties']?['severity'], 'Unknown')}",
"description": "@{coalesce(replace(coalesce(triggerBody()?['object']?['properties']?['description'], ''), '\\\"', '\\\\\\\"'), 'No description provided')}",
"incident_number": "@{coalesce(triggerBody()?['object']?['properties']?['incidentNumber'], 'Unknown')}",
"incident_title": "@{coalesce(triggerBody()?['object']?['properties']?['title'], 'Untitled Incident')}",
"incident_id": "@{coalesce(last(split(triggerBody()?['object']?['id'], '/')), 'Unknown')}",
"incident_status": "@{coalesce(triggerBody()?['object']?['properties']?['status'], 'Unknown')}",
"assignee": "@{coalesce(triggerBody()?['object']?['properties']?['owner']?['userPrincipalName'], 'Unassigned')}",
"updated_at": "@{coalesce(triggerBody()?['object']?['properties']?['lastModifiedTimeUtc'], utcNow())}",
"tactics": "@{if(empty(triggerBody()?['object']?['properties']?['tactics']), '[]', string(triggerBody()?['object']?['properties']?['tactics']))}",
"classification": "@{coalesce(triggerBody()?['object']?['properties']?['classification'], 'Unclassified')}",
"classification_reason": "@{coalesce(triggerBody()?['object']?['properties']?['classificationReason'], 'Not specified')}",
"classification_comment": "@{coalesce(triggerBody()?['object']?['properties']?['classificationComment'], '')}",
"event_time": "@{utcNow()}"
}
}
Step 2: Creating Automation Rules in Sentinel
Next, we will create 3 Automation rules on Sentinel to fire when certain conditions are met to trigger our playbooks
2.1 Navigate to Automation Rules
- In the Microsoft Sentinel portal, go to the "Automation" section
- Click "Create" and select "Automation rule"
2.2 Create Automation Rules
Create three automation rules:
Rule 1: On Create Incident
- Name: "On Create Incident"
- Trigger: "When incident is created"
- Actions: Run playbook "create-incident"
Rule 2: On Assign Incident
- Name: "On Assign Incident"
- Trigger: "When incident is updated"
- Condition: Status changes to "Active" Or Owner “Changed”
- Actions: Run playbook "assign-incident"
Rule 3: On Resolve Incident
- Name: "On Resolve Incident"
- Trigger: "When incident is updated"
- Condition: Status changes to "Closed"
- Actions: Run playbook "resolve-incident"
Step 3: Whitelists Azure Logic App IP Addresses into Kubernetes cluster Load Balancer
You should whitelist the Azure Logic App IP addresses in the Kubernetes cluster Load Balancer to allow the Logic App to communicate with the Penfield Platform.
-
You can find the Azure Logic App IP addresses by going to your Logic App -> Settings -> Networking -> Outbound addresses.
Note: The IP addresses may change over time, so it is recommended to monitor and update the whitelist as needed.
-
Whitelist the above IP addresses in the Kubernetes cluster Load Balancer security group.
Permissions and Testing
- Ensure the Logic App has the following permissions:
- "Logic App Contributor" role
- "Microsoft Sentinel Contributor" role
- Test the integration by:
- Creating a new incident in Sentinel
- Assigning the incident to a user
- Closing the incident
- Verify that the corresponding events are received in the Penfield Platform
Troubleshooting
- Check Logic App run history for any errors
- Verify API key and endpoint are correct
- Ensure proper permissions are set for the Logic App