Azure Logic Apps
Integrating a SailPoint Entro webhook with Azure Logic Apps enables automated workflows triggered by specific events.

Setup
- Login to the Azure portal and head to Logic Apps
-
Click on "+ Add" and select a hosting model
- The Consumption model is an inexpensive and effective choice for webhooks
-
Create the App at the desired location
-
Head over to the Logic app designer
.png)
The Logic app designer is where an Azure Logic Apps flow can be created. We will create a webhook receiver in order to push alerts from SailPoint Entro to the app.
-
Click on Add a Trigger
.png)
-
Search for Request and select "When an HTTP request is received"
.png)
-
Save the App and a URL will be generated
.png)
.png)
-
Copy the URL
- In the SailPoint Entro platform, go to Settings > Webhooks and add the URL as a Custom webhook
- Now, your Azure Logic Apps will receive risk events from SailPoint Entro
Example Flow
Copy and paste the following JSON into the App's "Logic app code view" to view the full example pictured at the begging of the article. The App will trigger only on newly created risks. To trigger it on updated risks, use the "updatedRisk" key instead of the "createdRisk" key.
Azure Logic Apps Flow JSON
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"triggers": {
"Entro_Webhook_Received": {
"type": "Request",
"kind": "Http",
"inputs": {
"method": "POST",
"schema": {
"type": "object",
"properties": {
"createdRisk": {
"type": "object",
"properties": {
"guid": {
"type": "string"
},
"severity": {
"type": "string"
},
"owner": {
"type": "string"
},
"detectionTime": {
"type": "integer"
},
"creationDate": {
"type": "integer"
},
"modifyDate": {
"type": "integer"
},
"source": {
"type": "string"
},
"isArchived": {
"type": "boolean"
},
"type": {
"type": "string"
},
"category": {
"type": "string"
},
"ruleCode": {
"type": "string"
},
"status": {
"type": "string"
},
"name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"ownerUid": {
"type": "string"
},
"path": {
"type": "string"
},
"mitigation": {
"type": "string"
},
"ownerAiObject": {
"type": "object",
"properties": {
"similarity": {
"type": "integer"
},
"ownerItemType": {
"type": "string"
},
"elementItemType": {
"type": "string"
},
"originalOwnerValue": {
"type": "string"
},
"originalElementValue": {
"type": "string"
},
"processedOwnerValue": {
"type": "string"
},
"processedElementValue": {
"type": "string"
}
}
},
"employee": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"status": {
"type": "string"
},
"title": {},
"division": {},
"organization": {},
"manager": {},
"creationDate": {},
"lastLogin": {}
}
},
"payload": {
"type": "array",
"items": {
"type": "object",
"properties": {
"exposedLowConfidence": {
"type": "boolean"
},
"exposedGuid": {
"type": "string"
},
"exposedStatus": {
"type": "string"
},
"exposureUrl": {
"type": "string"
},
"exposedStatusEnrichment": {
"type": "object",
"properties": {
"isValid": {
"type": "boolean"
},
"data": {
"type": "object",
"properties": {
"$metadata": {
"type": "object",
"properties": {
"httpStatusCode": {
"type": "integer"
},
"requestId": {
"type": "string"
},
"attempts": {
"type": "integer"
},
"totalRetryDelay": {
"type": "integer"
}
}
},
"UserId": {
"type": "string"
},
"Account": {
"type": "string"
},
"Arn": {
"type": "string"
}
}
},
"date": {
"type": "integer"
},
"TokenAwsAccount": {
"type": "string"
},
"TokenAccountEnvironment": {
"type": "string"
}
}
},
"exposedType": {
"type": "string"
},
"exposedOrigin": {
"type": "string"
},
"exposedValueHashed": {
"type": "string"
},
"exposedSnippet": {
"type": "string"
},
"exposedRedactedSnippet": {
"type": "string"
},
"exposedAwsAccessKeyId": {
"type": "string"
},
"vendorHash": {},
"account": {
"type": "object",
"properties": {
"environmentType": {
"type": "string"
},
"environment": {
"type": "string"
},
"accountId": {
"type": "string"
},
"accountType": {
"type": "string"
},
"tags": {
"type": "array"
}
}
},
"githubTargetWorkflowFileUrl": {
"type": "string"
},
"githubLastWorkflowRunUrl": {
"type": "string"
},
"githubLastJobUrl": {
"type": "string"
},
"githubRepositoryUrl": {
"type": "string"
},
"targetToken": {
"type": "string"
},
"awsArn": {
"type": "string"
}
},
"required": [
"account"
]
}
}
}
},
"notificationType": {
"type": "string"
}
}
}
}
}
},
"actions": {
"Initialize_variables": {
"runAfter": {},
"type": "InitializeVariable",
"inputs": {
"variables": [
{
"name": "foundUser",
"type": "boolean",
"value": false
},
{
"name": "SlackChannelID",
"type": "string"
}
]
}
},
"Is_Exposed_Secret": {
"actions": {
"For_each": {
"foreach": "@triggerOutputs()?['body']?['createdRisk']?['payload']",
"actions": {
"Is_Stream": {
"actions": {
"Get_User": {
"type": "Http",
"inputs": {
"uri": "https://slack.com/api/users.lookupByEmail",
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "email=@{triggerBody()?['createdRisk']?['employee']?['email']}&token=@{body('Get_Slack_API_Key')?['value']}"
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
},
"Parse_User_From_Slack": {
"runAfter": {
"Get_User": [
"Succeeded"
]
},
"type": "ParseJson",
"inputs": {
"content": "@body('Get_User')",
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"is_bot": {
"type": "boolean"
},
"updated": {
"type": "integer"
},
"is_app_user": {
"type": "boolean"
},
"team_id": {
"type": "string"
},
"deleted": {
"type": "boolean"
},
"color": {
"type": "string"
},
"is_email_confirmed": {
"type": "boolean"
},
"real_name": {
"type": "string"
},
"tz": {
"type": "string"
},
"tz_label": {
"type": "string"
},
"tz_offset": {
"type": "integer"
},
"is_admin": {
"type": "boolean"
},
"is_owner": {
"type": "boolean"
},
"is_primary_owner": {
"type": "boolean"
},
"is_restricted": {
"type": "boolean"
},
"is_ultra_restricted": {
"type": "boolean"
},
"who_can_share_contact_card": {
"type": "string"
},
"profile": {
"type": "object",
"properties": {
"real_name": {
"type": "string"
},
"display_name": {
"type": "string"
},
"avatar_hash": {
"type": "string"
},
"real_name_normalized": {
"type": "string"
},
"display_name_normalized": {
"type": "string"
},
"image_24": {
"type": "string"
},
"image_32": {
"type": "string"
},
"image_48": {
"type": "string"
},
"image_72": {
"type": "string"
},
"image_192": {
"type": "string"
},
"image_512": {
"type": "string"
},
"image_1024": {
"type": "string"
},
"image_original": {
"type": "string"
},
"is_custom_image": {
"type": "boolean"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"team": {
"type": "string"
},
"email": {
"type": "string"
},
"title": {
"type": "string"
},
"phone": {
"type": "string"
},
"skype": {
"type": "string"
},
"status_text": {
"type": "string"
},
"status_text_canonical": {
"type": "string"
},
"status_emoji": {
"type": "string"
},
"status_emoji_display_info": {
"type": "array"
},
"status_expiration": {
"type": "integer"
},
"huddle_state": {
"type": "string"
},
"huddle_state_expiration_ts": {
"type": "integer"
}
}
}
}
}
}
}
}
},
"User_Exists": {
"actions": {
"Set_variable": {
"runAfter": {
"Log_sent_to_owner_in_Entro": [
"Succeeded"
]
},
"type": "SetVariable",
"inputs": {
"name": "foundUser",
"value": true
}
},
"Send_to_Owner": {
"type": "Http",
"inputs": {
"uri": "https://slack.com/api/chat.postMessage",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer @{body('Get_Slack_API_Key')?['value']}"
},
"body": {
"channel": "@{body('Parse_User_From_Slack')?['user']?['id']}",
"text": "Hello @{body('Parse_User_From_Slack')?['user']?['name']}, a new exposure risk has been detected that requires your attention! 🚨",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Hello @{body('Parse_User_From_Slack')?['user']?['name']}, New Exposure Risk Detected! 🚨"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*View Risk in Entro:*\n<https://app.entro.security/admin/exposed-risks?riskGuid=@{triggerBody()?['createdRisk']?['guid']}|Click here to view the risk details>"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hi @{triggerBody()?['createdRisk']?['owner']}, this is an urgent notification regarding a new exposure risk that has been identified. Please review the details below and take appropriate action."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Risk Name:*\n@{triggerBody()?['createdRisk']?['name']}"
},
{
"type": "mrkdwn",
"text": "*Severity:*\n@{triggerBody()?['createdRisk']?['severity']}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Type:*\n@{triggerBody()?['createdRisk']?['type']}"
},
{
"type": "mrkdwn",
"text": "*Category:*\n@{triggerBody()?['createdRisk']?['category']}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Source:*\n@{triggerBody()?['createdRisk']?['source']}"
},
{
"type": "mrkdwn",
"text": "*Owner:*\n@{triggerBody()?['createdRisk']?['owner']}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Path:*\n@{triggerBody()?['createdRisk']?['path']}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Mitigation:*\n@{triggerBody()?['createdRisk']?['mitigation']}"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Timestamps:*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Detected At:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['detectionTime']), 'yyyy-MM-dd HH:mm:ss Z')}"
},
{
"type": "mrkdwn",
"text": "*Created At:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['creationDate']), 'yyyy-MM-dd HH:mm:ss Z')}"
},
{
"type": "mrkdwn",
"text": "*Last Modified:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['modifyDate']), 'yyyy-MM-dd HH:mm:ss Z')}"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Payload Details (Exposures):*"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "```\n@{triggerBody()?['createdRisk']?['payload']}\n```"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "ℹ️ This alert was generated by an automated system."
}
]
}
]
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
},
"Log_sent_to_owner_in_Entro": {
"runAfter": {
"Send_to_Owner": [
"Succeeded"
]
},
"type": "Http",
"inputs": {
"uri": "https://api.entro.security/v1/risk@{triggerBody()?['createdRisk']?['guid']}/comment",
"method": "POST",
"headers": {
"Authorization": "@{body('Get_Entro_API_Key')?['value']}",
"Content-Type": "application/json"
},
"body": {
"entroUser": "entro-automation",
"message": "Automation notified @{body('Parse_User_From_Slack')?['user']?['name']}"
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
}
},
"runAfter": {
"Parse_User_From_Slack": [
"Succeeded"
]
},
"else": {
"actions": {}
},
"expression": {
"and": [
{
"not": {
"equals": [
"@body('Parse_User_From_Slack')?['user']?['id']",
"@null"
]
}
}
]
},
"type": "If"
},
"Notify_Channel": {
"runAfter": {
"User_Exists": [
"Succeeded"
]
},
"type": "Http",
"inputs": {
"uri": "https://slack.com/api/chat.postMessage",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer @{body('Get_Slack_API_Key')?['value']}"
},
"body": {
"channel": "@{variables('SlackChannelID')}",
"text": "🚨 New Exposure Risk Detected! 🚨",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 New Exposure Risk Detected! 🚨"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*View Risk in Entro:*\n<https://app.entro.security/admin/exposed-risks?riskGuid=@{triggerBody()?['createdRisk']?['guid']}|Click here to view the risk details>"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "A new exposure risk has been identified that requires attention. Please review the details below."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Risk Name:*\n@{triggerBody()?['createdRisk']?['name']}"
},
{
"type": "mrkdwn",
"text": "*Severity:*\n@{triggerBody()?['createdRisk']?['severity']}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Type:*\n@{triggerBody()?['createdRisk']?['type']}"
},
{
"type": "mrkdwn",
"text": "*Category:*\n@{triggerBody()?['createdRisk']?['category']}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Source:*\n@{triggerBody()?['createdRisk']?['source']}"
},
{
"type": "mrkdwn",
"text": "*Owner:*\n@{triggerBody()?['createdRisk']?['owner']}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Path:*\n@{triggerBody()?['createdRisk']?['path']}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Mitigation:*\n@{triggerBody()?['createdRisk']?['mitigation']}"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Timestamps:*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Detected At:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['detectionTime']), 'yyyy-MM-dd HH:mm:ss Z')}"
},
{
"type": "mrkdwn",
"text": "*Created At:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['creationDate']), 'yyyy-MM-dd HH:mm:ss Z')}"
},
{
"type": "mrkdwn",
"text": "*Last Modified:*\n@{formatDateTime(addseconds('1970-01-01T00:00:00Z', triggerBody()?['createdRisk']?['modifyDate']), 'yyyy-MM-dd HH:mm:ss Z')}"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Payload Details (Exposures):*"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "```\n@{triggerBody()?['createdRisk']?['payload']}\n```"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "ℹ️ This alert was generated by an automated system."
},
{
"type": "mrkdwn",
"text": "👤 *Slack User Found:*\n@{variables('foundUser')}"
}
]
}
]
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
},
"Log_channel_notified": {
"runAfter": {
"Notify_Channel": [
"Succeeded"
]
},
"type": "Http",
"inputs": {
"uri": "https://api.entro.security/v1/risk@{triggerBody()?['createdRisk']?['guid']}/comment",
"method": "POST",
"headers": {
"Authorization": "@{body('Get_Entro_API_Key')?['value']}",
"Content-Type": "application/json"
},
"body": {
"entroUser": "entro-automation",
"message": "Automation notified @{variables('SlackChannelID')}"
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
}
},
"else": {
"actions": {}
},
"expression": {
"or": [
{
"equals": [
true,
true
]
},
{
"and": [
{
"not": {
"equals": [
"@item()?['exposureUrl']",
"@null"
]
}
},
{
"contains": [
"@toLower(item()?['exposureUrl'])",
"stream"
]
}
]
},
{
"and": [
{
"not": {
"equals": [
"@triggerBody()?['createdRisk']?['path']",
"@null"
]
}
},
{
"contains": [
"@toLower(triggerBody()?['createdRisk']?['path'])",
"stream"
]
}
]
},
{
"and": [
{
"not": {
"equals": [
"@item()?['githubRepositoryUrl']",
"@null"
]
}
},
{
"contains": [
"@toLower(item()?['githubRepositoryUrl'])",
"stream"
]
}
]
}
]
},
"type": "If"
}
},
"type": "Foreach"
}
},
"runAfter": {
"Get_Entro_API_Key": [
"Succeeded"
]
},
"else": {
"actions": {}
},
"expression": {
"and": [
{
"not": {
"equals": [
"@triggerBody()?['createdRisk']?['ruleCode']",
"OVEREXPOSED"
]
}
},
{
"not": {
"equals": [
"@triggerBody()?['createdRisk']?['ruleCode']",
"GOLD_MINE"
]
}
},
{
"equals": [
"@triggerBody()?['createdRisk']?['category']",
"EXPOSED_SECRET"
]
}
]
},
"type": "If"
},
"Get_Slack_API_Key": {
"runAfter": {
"Initialize_variables": [
"Succeeded"
]
},
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['keyvault']['connectionId']"
}
},
"method": "get",
"path": "/secrets/@{encodeURIComponent('SlackAPIKey')}/value"
},
"runtimeConfiguration": {
"secureData": {
"properties": [
"inputs",
"outputs"
]
}
}
},
"Get_Entro_API_Key": {
"runAfter": {
"Get_Slack_API_Key": [
"Succeeded"
]
},
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['keyvault']['connectionId']"
}
},
"method": "get",
"path": "/secrets/@{encodeURIComponent('EntroAPIKey')}/value"
},
"runtimeConfiguration": {
"secureData": {
"properties": [
"inputs",
"outputs"
]
}
}
}
},
"outputs": {},
"parameters": {
"$connections": {
"type": "Object",
"defaultValue": {}
}
}
},
"parameters": {
"$connections": {
"type": "Object",
"value": {
"keyvault": {
"id": "/subscriptions/xxxxxxx/providers/Microsoft.Web/locations/eastus/managedApis/keyvault",
"connectionId": "/xxxxxxxx/resourceGroups/EntroSecurityRG/providers/Microsoft.Web/connections/keyvault",
"connectionName": "keyvault",
"connectionProperties": {
"authentication": {
"type": "ManagedServiceIdentity"
}
}
}
}
}
}
}
.png)