From 188f9199bfc1fbcaf8b423855f537fd559c18791 Mon Sep 17 00:00:00 2001 From: Niklas Logren Date: Mon, 15 Jun 2026 11:32:12 +0200 Subject: [PATCH 1/4] feat: migrate v3.0 playbooks to Log Ingestion API (DCE/DCR) --- .../Data Connectors/azuredeploy-v3.json | 431 ++++++++++++++++++ .../RFI-lookup-and-save-user/azuredeploy.json | 121 +++-- .../RFI-search-external-user/azuredeploy.json | 114 +++-- .../azuredeploy.json | 170 ++++--- .../Playbooks/v3.0/readme.md | 78 +++- 5 files changed, 761 insertions(+), 153 deletions(-) create mode 100644 Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json diff --git a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json new file mode 100644 index 00000000000..10383b3ec1f --- /dev/null +++ b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json @@ -0,0 +1,431 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "Recorded Future Identity v3.0 - Data Connectors", + "description": "Deploys shared infrastructure (DCE, DCRs, LA tables) used by the RFI v3.0 playbooks (RFI-lookup-and-save-user, RFI-search-external-user, RFI-search-workforce-user) to write credential exposure data to Log Analytics via the Azure Monitor Logs Ingestion API.", + "lastUpdateTime": "2026-06-15T00:00:00.000Z", + "support": { + "tier": "Partner" + }, + "author": { + "name": "Recorded Future" + }, + "releaseNotes": [ + { + "version": "1.0", + "title": "Initial version", + "notes": [ + "DCE and DCRs for RFI-lookup-and-save-user, RFI-search-external-user, and RFI-search-workforce-user.", + "Creates three Log Analytics tables: UsersLookupResults, MalwareLogs, CredentialDumps." + ] + } + ] + }, + "parameters": { + "log_analytics_workspace_name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics Workspace where the tables will be created. Must be in the same resource group as this deployment." + } + }, + "log_analytics_workspace_location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the Log Analytics Workspace. Defaults to the resource group location." + } + } + }, + "variables": { + "DceName": "recorded-future-identity-dce", + "DcrLookupName": "recorded-future-identity-v3-dcr-lookup-results", + "DcrMalwareLogsName": "recorded-future-identity-v3-dcr-malware-logs", + "DcrCredentialDumpsName": "recorded-future-identity-v3-dcr-credential-dumps", + "TableLookupName": "RFI_UsersLookupResults_V2_CL", + "TableMalwareLogsName": "RFI_MalwareLogs_V2_CL", + "TableCredentialDumpsName": "RFI_CredentialDumps_V2_CL", + "StreamLookupName": "Custom-RFI_UsersLookupResults_V2_CL", + "StreamMalwareLogsName": "Custom-RFI_MalwareLogs_V2_CL", + "StreamCredentialDumpsName": "Custom-RFI_CredentialDumps_V2_CL", + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('log_analytics_workspace_name'))]", + "dceResourceId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', variables('DceName'))]", + "dcrLookupResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrLookupName'))]", + "dcrMalwareLogsResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrMalwareLogsName'))]", + "dcrCredentialDumpsResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrCredentialDumpsName'))]", + "tableLookupResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('log_analytics_workspace_name'), variables('TableLookupName'))]", + "tableMalwareLogsResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('log_analytics_workspace_name'), variables('TableMalwareLogsName'))]", + "tableCredentialDumpsResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('log_analytics_workspace_name'), variables('TableCredentialDumpsName'))]" + }, + "resources": [ + { + "type": "Microsoft.Insights/dataCollectionEndpoints", + "apiVersion": "2024-03-11", + "name": "[variables('DceName')]", + "location": "[resourceGroup().location]", + "properties": { + "networkAcls": { + "publicNetworkAccess": "Enabled" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2022-10-01", + "name": "[concat(parameters('log_analytics_workspace_name'), '/', variables('TableLookupName'))]", + "properties": { + "schema": { + "name": "[variables('TableLookupName')]", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "risky_user_email", + "type": "string" + }, + { + "name": "data", + "type": "dynamic" + } + ] + }, + "retentionInDays": 90, + "plan": "Analytics" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2022-10-01", + "name": "[concat(parameters('log_analytics_workspace_name'), '/', variables('TableMalwareLogsName'))]", + "properties": { + "schema": { + "name": "[variables('TableMalwareLogsName')]", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "login", + "type": "string" + }, + { + "name": "domain", + "type": "string" + } + ] + }, + "retentionInDays": 90, + "plan": "Analytics" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2022-10-01", + "name": "[concat(parameters('log_analytics_workspace_name'), '/', variables('TableCredentialDumpsName'))]", + "properties": { + "schema": { + "name": "[variables('TableCredentialDumpsName')]", + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "email", + "type": "string" + } + ] + }, + "retentionInDays": 90, + "plan": "Analytics" + } + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2024-03-11", + "name": "[variables('DcrLookupName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[variables('dceResourceId')]", + "[variables('tableLookupResourceId')]" + ], + "properties": { + "dataCollectionEndpointId": "[variables('dceResourceId')]", + "streamDeclarations": { + "[variables('StreamLookupName')]": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "risky_user_email", + "type": "string" + }, + { + "name": "data", + "type": "dynamic" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[variables('workspaceResourceId')]", + "name": "workspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "[variables('StreamLookupName')]" + ], + "destinations": [ + "workspace" + ], + "transformKql": "source | project TimeGenerated = now(), risky_user_email = tostring(risky_user_email), data = todynamic(data)", + "outputStream": "[variables('StreamLookupName')]" + } + ] + } + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2024-03-11", + "name": "[variables('DcrMalwareLogsName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[variables('dceResourceId')]", + "[variables('tableMalwareLogsResourceId')]" + ], + "properties": { + "dataCollectionEndpointId": "[variables('dceResourceId')]", + "streamDeclarations": { + "[variables('StreamMalwareLogsName')]": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "login", + "type": "string" + }, + { + "name": "domain", + "type": "string" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[variables('workspaceResourceId')]", + "name": "workspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "[variables('StreamMalwareLogsName')]" + ], + "destinations": [ + "workspace" + ], + "transformKql": "source | project TimeGenerated = now(), login = tostring(login), domain = tostring(domain)", + "outputStream": "[variables('StreamMalwareLogsName')]" + } + ] + } + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2024-03-11", + "name": "[variables('DcrCredentialDumpsName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[variables('dceResourceId')]", + "[variables('tableCredentialDumpsResourceId')]" + ], + "properties": { + "dataCollectionEndpointId": "[variables('dceResourceId')]", + "streamDeclarations": { + "[variables('StreamCredentialDumpsName')]": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "email", + "type": "string" + } + ] + } + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[variables('workspaceResourceId')]", + "name": "workspace" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "[variables('StreamCredentialDumpsName')]" + ], + "destinations": [ + "workspace" + ], + "transformKql": "source | project TimeGenerated = now(), email = tostring(email)", + "outputStream": "[variables('StreamCredentialDumpsName')]" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions", + "apiVersion": "2022-09-01-preview", + "name": "[concat(parameters('log_analytics_workspace_name'), '/Microsoft.SecurityInsights/RecordedFutureIdentityV3')]", + "location": "[parameters('log_analytics_workspace_location')]", + "kind": "Customizable", + "properties": { + "connectorUiConfig": { + "id": "RecordedFutureIdentityV3", + "title": "Recorded Future Identity - Credential Exposure Importer", + "publisher": "Recorded Future", + "descriptionMarkdown": "Imports Recorded Future Identity credential exposure data into Microsoft Sentinel via the Azure Monitor Logs Ingestion API. Writes malware log exposures to `RFI_MalwareLogs_V2_CL`, credential dump exposures to `RFI_CredentialDumps_V2_CL`, and user lookup results to `RFI_UsersLookupResults_V2_CL`.", + "graphQueries": [ + { + "metricName": "Malware Log Exposures", + "legend": "RFI_MalwareLogs_V2_CL", + "baseQuery": "RFI_MalwareLogs_V2_CL" + }, + { + "metricName": "Credential Dump Exposures", + "legend": "RFI_CredentialDumps_V2_CL", + "baseQuery": "RFI_CredentialDumps_V2_CL" + }, + { + "metricName": "User Lookup Results", + "legend": "RFI_UsersLookupResults_V2_CL", + "baseQuery": "RFI_UsersLookupResults_V2_CL" + } + ], + "sampleQueries": [ + { + "description": "Recent malware log exposures", + "query": "RFI_MalwareLogs_V2_CL\n| sort by TimeGenerated desc" + }, + { + "description": "Recent credential dump exposures", + "query": "RFI_CredentialDumps_V2_CL\n| sort by TimeGenerated desc" + }, + { + "description": "User lookup results", + "query": "RFI_UsersLookupResults_V2_CL\n| sort by TimeGenerated desc" + } + ], + "dataTypes": [ + { + "name": "RFI_MalwareLogs_V2_CL", + "lastDataReceivedQuery": "RFI_MalwareLogs_V2_CL\n| summarize Time = max(TimeGenerated)\n| where isnotempty(Time)" + }, + { + "name": "RFI_CredentialDumps_V2_CL", + "lastDataReceivedQuery": "RFI_CredentialDumps_V2_CL\n| summarize Time = max(TimeGenerated)\n| where isnotempty(Time)" + }, + { + "name": "RFI_UsersLookupResults_V2_CL", + "lastDataReceivedQuery": "RFI_UsersLookupResults_V2_CL\n| summarize Time = max(TimeGenerated)\n| where isnotempty(Time)" + } + ], + "connectivityCriteria": [ + { + "type": "IsConnectedQuery", + "value": [ + "RFI_MalwareLogs_V2_CL\n| summarize LastLog = max(TimeGenerated)\n| where LastLog >= ago(24h)" + ] + } + ], + "availability": { + "status": 1 + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "Read and write permissions are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true + } + } + ], + "customs": [ + { + "name": "Azure Subscription", + "description": "Monitoring Contributor and Log Analytics Contributor permissions required to deploy the Data Connectors infrastructure. Owner or Role Based Access Control Administrator required to deploy playbooks with automatic role assignment." + }, + { + "name": "Recorded Future API Token", + "description": "A Recorded Future Identity API token is required to authorize the RFI Custom Connector used by the playbooks." + } + ] + }, + "instructionSteps": [ + { + "title": "Step 1 \u2014 Deploy Data Connectors infrastructure", + "description": "Deploys the shared Data Collection Endpoint (DCE), Data Collection Rules (DCRs), Log Analytics tables, and this connector definition tile.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FSolutions%2FRecorded%20Future%20Identity%2FData%20Connectors%2Fazuredeploy-v3.json)" + }, + { + "title": "Step 2 \u2014 Deploy the playbooks", + "description": "Deploy one or more of the following playbooks. See the [v3.0 readme](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Recorded%20Future%20Identity/Playbooks/v3.0/readme.md) for full deployment instructions.\n\n- `RFI-search-workforce-user` \u2014 searches for compromised workforce users\n- `RFI-search-external-user` \u2014 searches for compromised external/customer users\n- `RFI-lookup-and-save-user` \u2014 fetches detailed lookup data for a specific user" + } + ] + } + } + } + ], + "outputs": { + "dceEndpoint": { + "type": "string", + "value": "[reference(variables('dceResourceId'), '2024-03-11').logsIngestion.endpoint]" + }, + "dcrLookupImmutableId": { + "type": "string", + "value": "[reference(variables('dcrLookupResourceId'), '2024-03-11').immutableId]" + }, + "dcrMalwareLogsImmutableId": { + "type": "string", + "value": "[reference(variables('dcrMalwareLogsResourceId'), '2024-03-11').immutableId]" + }, + "dcrCredentialDumpsImmutableId": { + "type": "string", + "value": "[reference(variables('dcrCredentialDumpsResourceId'), '2024-03-11').immutableId]" + }, + "streamLookupName": { + "type": "string", + "value": "[variables('StreamLookupName')]" + }, + "streamMalwareLogsName": { + "type": "string", + "value": "[variables('StreamMalwareLogsName')]" + }, + "streamCredentialDumpsName": { + "type": "string", + "value": "[variables('StreamCredentialDumpsName')]" + } + } +} \ No newline at end of file diff --git a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-lookup-and-save-user/azuredeploy.json b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-lookup-and-save-user/azuredeploy.json index 6cd0b4bf845..8f8b09151ba 100644 --- a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-lookup-and-save-user/azuredeploy.json +++ b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-lookup-and-save-user/azuredeploy.json @@ -1,18 +1,19 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.2.0.0", + "contentVersion": "1.3.0.0", "metadata": { "title": "RFI-lookup-and-save-user", "description": "This playbook gets compromise identity details from Recorded Future Identity Intelligence and saves the data for further review and analysis.", "prerequisites": [ "The custom connector RFI-CustomConnector-0-1-0 have to be deployed under the same subscription.", - "To use the Recorded Future for Azure connector, you will need a valid API token from Recorded Future as described in the [documentation](https://learn.microsoft.com/en-us/connectors/recordedfuturev2/#how-to-get-credentials)" + "To use the Recorded Future for Azure connector, you will need a valid API token from Recorded Future as described in the [documentation](https://learn.microsoft.com/en-us/connectors/recordedfuturev2/#how-to-get-credentials)", + "The v3.0 Data Connectors infrastructure (azuredeploy-v3.json) must be deployed before this playbook." ], "postDeployment": [ "After deployment, open the playbook to configure all connections and press save." ], "prerequisitesDeployTemplateFile": "../RFI-CustomConnector-0-1-0/azuredeploy.json", - "lastUpdateTime": "2024-06-11T14:25:00.000Z", + "lastUpdateTime": "2026-06-15T00:00:00.000Z", "entities": [], "tags": [ "Identity protection" ], "support": { @@ -36,6 +37,11 @@ "version": "1.2", "title": "Identity endpoint update", "notes": [ "Updated lookup envpoint to new version. Structure of data in the lookup_results_log_analytics_custom_log_name " ] + }, + { + "version": "1.3", + "title": "Migrated to Log Ingestion API", + "notes": [ "Replaced Azure Log Analytics Data Collector connector with HTTP action using Managed Identity and the Azure Monitor Logs Ingestion API (DCE/DCR)." ] } ] }, @@ -50,11 +56,29 @@ "metadata": { "description": "Name of the logic app connector which performs Recorded Future Communication. Normaly this dont change from RFI-CustomConnector-0-1-0" } + }, + "log_analytics_workspace_name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics Workspace. Used to resolve the DCE and DCR deployed by azuredeploy-v3.json." + } + }, + "create_role_assignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "If true, automatically assigns the Monitoring Metrics Publisher role on the DCR to the Logic App managed identity. Requires Owner or Role Based Access Control Administrator on the resource group. Set to false to assign the role manually after deployment." + } } }, "variables": { "IdentityconnectorupdateConnectionName": "RFI-CustomConnector-0-1-0", - "AzureloganalyticsdatacollectorConnectionName": "[concat('Azureloganalyticsdatacollector-', parameters('PlaybookName'))]" + "MonitoringMetricsPublisherRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "DceName": "recorded-future-identity-dce", + "DcrName": "recorded-future-identity-v3-dcr-lookup-results", + "StreamName": "Custom-RFI_UsersLookupResults_V2_CL", + "dceResourceId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', variables('DceName'))]", + "dcrResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrName'))]" }, "resources": [ { @@ -70,12 +94,21 @@ "type": "Object" }, "lookup_results_log_analytics_custom_log_name_default": { - "defaultValue": "RecordedFutureIdentity_UsersLookupResults_CL", + "defaultValue": "RFI_UsersLookupResults_V2_CL", "type": "String" }, "lookup_lookback_days_default": { "defaultValue": -14, "type": "Int" + }, + "DceEndpoint": { + "type": "String" + }, + "DcrImmutableId": { + "type": "String" + }, + "StreamName": { + "type": "String" } }, "triggers": { @@ -323,26 +356,36 @@ "statusCode": 200 } }, - "Send_Data_-_Save_Lookup_results_to_LogAnalytics_Custom_Log": { + "Compose_lookup_row": { "runAfter": { "Response_-_Failed_to_get_Lookup_data": [ "Skipped" ] }, - "type": "ApiConnection", + "type": "Compose", "inputs": { - "body": "@{body('Credential_Lookup_V2_-_Look_up_credential_data_for_one_or_more_users')}", + "risky_user_email": "@triggerBody()?['risky_user_email']", + "data": "@body('Credential_Lookup_V2_-_Look_up_credential_data_for_one_or_more_users')" + } + }, + "Send_Data_-_Save_Lookup_results_to_LogAnalytics_Custom_Log": { + "runAfter": { + "Compose_lookup_row": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{parameters('DceEndpoint')}/dataCollectionRules/@{parameters('DcrImmutableId')}/streams/@{parameters('StreamName')}?api-version=2023-01-01", "headers": { - "Log-Type": "@{if(equals(triggerBody()?['lookup_results_log_analytics_custom_log_name'], null), parameters('lookup_results_log_analytics_custom_log_name_default'), triggerBody()?['lookup_results_log_analytics_custom_log_name'])}", - "time-generated-field": "@{utcNow()}" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['azureloganalyticsdatacollector_1']['connectionId']" - } + "Content-Type": "application/json" }, - "method": "post", - "path": "/api/logs" + "body": "@createArray(outputs('Compose_lookup_row'))", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://monitor.azure.com" + } } } } @@ -354,13 +397,17 @@ "connectionId": "[resourceId('Microsoft.Web/connections', variables('IdentityconnectorupdateConnectionName'))]", "connectionName": "[variables('IdentityconnectorupdateConnectionName')]", "id": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/customApis/', parameters('IdentityCustomConnectorName'))]" - }, - "azureloganalyticsdatacollector_1": { - "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureloganalyticsdatacollectorConnectionName'))]", - "connectionName": "[variables('AzureloganalyticsdatacollectorConnectionName')]", - "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azureloganalyticsdatacollector')]" } } + }, + "DceEndpoint": { + "value": "[reference(variables('dceResourceId'), '2024-03-11').logsIngestion.endpoint]" + }, + "DcrImmutableId": { + "value": "[reference(variables('dcrResourceId'), '2024-03-11').immutableId]" + }, + "StreamName": { + "value": "[variables('StreamName')]" } } }, @@ -369,15 +416,14 @@ "location": "[resourceGroup().location]", "tags": { "hidden-SentinelTemplateName": "RFI-lookup-and-save-user", - "hidden-SentinelTemplateVersion": "1.2" + "hidden-SentinelTemplateVersion": "1.3" }, "identity": { "type": "SystemAssigned" }, - "apiVersion": "2017-07-01", + "apiVersion": "2019-05-01", "dependsOn": [ - "[resourceId('Microsoft.Web/connections', variables('IdentityconnectorupdateConnectionName'))]", - "[resourceId('Microsoft.Web/connections', variables('AzureloganalyticsdatacollectorConnectionName'))]" + "[resourceId('Microsoft.Web/connections', variables('IdentityconnectorupdateConnectionName'))]" ] }, { @@ -395,18 +441,19 @@ } }, { - "type": "Microsoft.Web/connections", - "apiVersion": "2016-06-01", - "name": "[variables('AzureloganalyticsdatacollectorConnectionName')]", - "location": "[resourceGroup().location]", - "kind": "V1", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(variables('dcrResourceId'), parameters('PlaybookName'), variables('MonitoringMetricsPublisherRoleId'))]", + "scope": "[variables('dcrResourceId')]", + "dependsOn": [ + "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + ], "properties": { - "displayName": "[variables('AzureloganalyticsdatacollectorConnectionName')]", - "customParameterValues": {}, - "api": { - "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azureloganalyticsdatacollector')]" - } - } + "roleDefinitionId": "[variables('MonitoringMetricsPublisherRoleId')]", + "principalId": "[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "condition": "[parameters('create_role_assignment')]" } ] } \ No newline at end of file diff --git a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-external-user/azuredeploy.json b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-external-user/azuredeploy.json index db84ff760d0..2e4facc5322 100644 --- a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-external-user/azuredeploy.json +++ b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-external-user/azuredeploy.json @@ -1,10 +1,10 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.1.0.0", + "contentVersion": "1.3.0.0", "metadata": { "title": "RFI-search-external-user", "description": "This playbook searches the Recorded Future Identity Intelligence Module for compromised external (customer) users.\n\nThis playbook depends on:\n- RFI-add-EntraID-security-group-user\n- RFI-confirm-EntraID-risky-user\n- RFI-lookup-and-save-user\n\n Those playbooks need to be installed **manually** before installing current playbook.", - "lastUpdateTime": "2024-08-27T14:25:00.000Z", + "lastUpdateTime": "2026-06-15T00:00:00.000Z", "entities": [], "tags": ["Identity protection"], "support": { @@ -28,6 +28,11 @@ "version": "1.2", "title": "Updates", "notes": [ "Added Log Analytic Workspace as a parameter." ] + }, + { + "version": "1.3", + "title": "Migrated to Log Ingestion API", + "notes": [ "Replaced Azure Log Analytics Data Collector connector with HTTP action using Managed Identity and the Azure Monitor Logs Ingestion API (DCE/DCR)." ] } ] }, @@ -54,12 +59,24 @@ "Playbook-Name-confirm-EntraID-risky-user": { "defaultValue": "RFI-confirm-EntraID-risky-user", "type": "string" + }, + "create_role_assignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "If true, automatically assigns the Monitoring Metrics Publisher role on the DCR to the Logic App managed identity. Requires Owner or Role Based Access Control Administrator on the resource group. Set to false to assign the role manually after deployment." + } } }, "variables": { - "LogAnalyticsDataCollectorConnectionName": "[concat('azureloganalyticsdatacollector-', parameters('PlaybookName'))]", "AzureMonitorLogsConnectionName": "[concat('azuremonitorlogs-', parameters('PlaybookName'))]", - "RecordedFutureIdentityConnectionName": "[concat('recordedfutureidenti-', parameters('PlaybookName'))]" + "RecordedFutureIdentityConnectionName": "[concat('recordedfutureidenti-', parameters('PlaybookName'))]", + "MonitoringMetricsPublisherRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "DceName": "recorded-future-identity-dce", + "DcrName": "recorded-future-identity-v3-dcr-malware-logs", + "StreamName": "Custom-RFI_MalwareLogs_V2_CL", + "dceResourceId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', variables('DceName'))]", + "dcrResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrName'))]" }, "resources": [ { @@ -68,10 +85,12 @@ "name": "[parameters('PlaybookName')]", "location": "[resourceGroup().location]", "tags": { - "hidden-SentinelTemplateVersion": "1.2" + "hidden-SentinelTemplateVersion": "1.3" + }, + "identity": { + "type": "SystemAssigned" }, "dependsOn": [ - "[resourceId('Microsoft.Web/connections', variables('LogAnalyticsDataCollectorConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('AzureMonitorLogsConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('RecordedFutureIdentityConnectionName'))]" ], @@ -94,11 +113,11 @@ "type": "Int" }, "malware_logs_log_analytics_custom_log_name": { - "defaultValue": "RecordedFutureIdentity_LeakedCredentials_MalwareLogs_CL", + "defaultValue": "RFI_MalwareLogs_V2_CL", "type": "String" }, "lookup_results_log_analytics_custom_log_name": { - "defaultValue": "RecordedFutureIdentity_UsersLookupResults_CL", + "defaultValue": "RFI_UsersLookupResults_V2_CL", "type": "String" }, "lookup_lookback_days": { @@ -112,6 +131,15 @@ "active_directory_security_group_id": { "defaultValue": "", "type": "String" + }, + "DceEndpoint": { + "type": "String" + }, + "DcrImmutableId": { + "type": "String" + }, + "StreamName": { + "type": "String" } }, "triggers": { @@ -192,7 +220,10 @@ "type": "AppendToArrayVariable", "inputs": { "name": "unknown_malware_log_creds", - "value": "@items('For_Each_new_Malware_log_exposures')" + "value": { + "login": "@items('For_Each_new_Malware_log_exposures')?['login']", + "domain": "@items('For_Each_new_Malware_log_exposures')?['domain']" + } } } }, @@ -376,7 +407,7 @@ }, "type": "ApiConnection", "inputs": { - "body": "@{parameters('malware_logs_log_analytics_custom_log_name')}\n| project login=login_s, domain=domain_s", + "body": "@{parameters('malware_logs_log_analytics_custom_log_name')}\n| project login=login, domain=domain", "host": { "connection": { "name": "@parameters('$connections')['azuremonitorlogs']['connectionId']" @@ -399,20 +430,18 @@ "Succeeded" ] }, - "type": "ApiConnection", + "type": "Http", "inputs": { - "body": "@{outputs('Transform_new_Malware_log_exposures_array_into_a_JSON_object')}", + "method": "POST", + "uri": "@{parameters('DceEndpoint')}/dataCollectionRules/@{parameters('DcrImmutableId')}/streams/@{parameters('StreamName')}?api-version=2023-01-01", "headers": { - "Log-Type": "@parameters('malware_logs_log_analytics_custom_log_name')", - "time-generated-field": "@{utcNow()}" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['azureloganalyticsdatacollector']['connectionId']" - } + "Content-Type": "application/json" }, - "method": "post", - "path": "/api/logs" + "body": "@outputs('Transform_new_Malware_log_exposures_array_into_a_JSON_object')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://monitor.azure.com" + } } }, "Transform_new_Malware_log_exposures_array_into_a_JSON_object": { @@ -430,11 +459,6 @@ "parameters": { "$connections": { "value": { - "azureloganalyticsdatacollector": { - "connectionId": "[resourceId('Microsoft.Web/connections', variables('LogAnalyticsDataCollectorConnectionName'))]", - "connectionName": "[variables('LogAnalyticsDataCollectorConnectionName')]", - "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/azureloganalyticsdatacollector')]" - }, "azuremonitorlogs": { "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureMonitorLogsConnectionName'))]", "connectionName": "[variables('AzureMonitorLogsConnectionName')]", @@ -446,22 +470,19 @@ "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/recordedfutureidenti')]" } } + }, + "DceEndpoint": { + "value": "[reference(variables('dceResourceId'), '2024-03-11').logsIngestion.endpoint]" + }, + "DcrImmutableId": { + "value": "[reference(variables('dcrResourceId'), '2024-03-11').immutableId]" + }, + "StreamName": { + "value": "[variables('StreamName')]" } } } }, - { - "type": "Microsoft.Web/connections", - "apiVersion": "2018-07-01-preview", - "name": "[variables('LogAnalyticsDataCollectorConnectionName')]", - "location": "[resourceGroup().location]", - "properties": { - "api": { - "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/azureloganalyticsdatacollector')]" - }, - "displayName": "[variables('LogAnalyticsDataCollectorConnectionName')]" - } - }, { "type": "Microsoft.Web/connections", "apiVersion": "2018-07-01-preview", @@ -485,6 +506,21 @@ }, "displayName": "[variables('RecordedFutureIdentityConnectionName')]" } - } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(variables('dcrResourceId'), parameters('PlaybookName'), variables('MonitoringMetricsPublisherRoleId'))]", + "scope": "[variables('dcrResourceId')]", + "dependsOn": [ + "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + ], + "properties": { + "roleDefinitionId": "[variables('MonitoringMetricsPublisherRoleId')]", + "principalId": "[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "condition": "[parameters('create_role_assignment')]" + } ] } \ No newline at end of file diff --git a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-workforce-user/azuredeploy.json b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-workforce-user/azuredeploy.json index cbec91d0844..dc5c4490081 100644 --- a/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-workforce-user/azuredeploy.json +++ b/Solutions/Recorded Future Identity/Playbooks/v3.0/RFI-search-workforce-user/azuredeploy.json @@ -1,10 +1,10 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.1.0.0", + "contentVersion": "1.3.0.0", "metadata": { "title": "RFI-search-workforce-user", "description": "This playbook searches the Recorded Future Identity Intelligence Module for compromised workforce users.\n\nThis playbook depends on:\n- RFI-add-EntraID-security-group-user\n- RFI-confirm-EntraID-risky-user\n- RFI-lookup-and-save-user\n\n Those playbooks need to be installed **manually** before installing current playbook.", - "lastUpdateTime": "2024-08-27T14:25:00.000Z", + "lastUpdateTime": "2026-06-15T00:00:00.000Z", "entities": [], "tags": ["Identity protection"], "support": { @@ -28,6 +28,11 @@ "version": "1.2", "title": "Updates", "notes": [ "Added workspace_name as a parameter." ] + }, + { + "version": "1.3", + "title": "Migrated to Log Ingestion API", + "notes": [ "Replaced Azure Log Analytics Data Collector connector with HTTP actions using Managed Identity and the Azure Monitor Logs Ingestion API (DCE/DCR)." ] } ] }, @@ -54,12 +59,27 @@ "Playbook-Name-confirm-EntraID-risky-user": { "defaultValue": "RFI-confirm-EntraID-risky-user", "type": "string" + }, + "create_role_assignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "If true, automatically assigns the Monitoring Metrics Publisher role on both DCRs to the Logic App managed identity. Requires Owner or Role Based Access Control Administrator on the resource group. Set to false to assign the roles manually after deployment." + } } }, "variables": { - "LogAnalyticsDataCollectorConnectionName": "[concat('azureloganalyticsdatacollector-', parameters('PlaybookName'))]", "AzureMonitorLogsConnectionName": "[concat('azuremonitorlogs-', parameters('PlaybookName'))]", - "RecordedFutureIdentityConnectionName": "[concat('recordedfutureidenti-', parameters('PlaybookName'))]" + "RecordedFutureIdentityConnectionName": "[concat('recordedfutureidenti-', parameters('PlaybookName'))]", + "MonitoringMetricsPublisherRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "DceName": "recorded-future-identity-dce", + "DcrMalwareLogsName": "recorded-future-identity-v3-dcr-malware-logs", + "DcrCredentialDumpsName": "recorded-future-identity-v3-dcr-credential-dumps", + "StreamMalwareLogsName": "Custom-RFI_MalwareLogs_V2_CL", + "StreamCredentialDumpsName": "Custom-RFI_CredentialDumps_V2_CL", + "dceResourceId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', variables('DceName'))]", + "dcrMalwareLogsResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrMalwareLogsName'))]", + "dcrCredentialDumpsResourceId": "[resourceId('Microsoft.Insights/dataCollectionRules', variables('DcrCredentialDumpsName'))]" }, "resources": [ { @@ -68,10 +88,12 @@ "name": "[parameters('PlaybookName')]", "location": "[resourceGroup().location]", "tags": { - "hidden-SentinelTemplateVersion": "1.2" + "hidden-SentinelTemplateVersion": "1.3" + }, + "identity": { + "type": "SystemAssigned" }, "dependsOn": [ - "[resourceId('Microsoft.Web/connections', variables('LogAnalyticsDataCollectorConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('AzureMonitorLogsConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('RecordedFutureIdentityConnectionName'))]" ], @@ -94,15 +116,15 @@ "type": "Int" }, "credential_dumps_log_analytics_custom_log_name": { - "defaultValue": "RecordedFutureIdentity_LeakedCredentials_CredentialDumps_CL", + "defaultValue": "RFI_CredentialDumps_V2_CL", "type": "String" }, "malware_logs_log_analytics_custom_log_name": { - "defaultValue": "RecordedFutureIdentity_LeakedCredentials_MalwareLogs_CL", + "defaultValue": "RFI_MalwareLogs_V2_CL", "type": "String" }, "lookup_results_log_analytics_custom_log_name": { - "defaultValue": "RecordedFutureIdentity_UsersLookupResults_CL", + "defaultValue": "RFI_UsersLookupResults_V2_CL", "type": "String" }, "lookup_lookback_days": { @@ -116,6 +138,21 @@ "active_directory_security_group_id": { "defaultValue": "", "type": "String" + }, + "DceEndpoint": { + "type": "String" + }, + "DcrMalwareLogsImmutableId": { + "type": "String" + }, + "DcrCredentialDumpsImmutableId": { + "type": "String" + }, + "StreamMalwareLogsName": { + "type": "String" + }, + "StreamCredentialDumpsName": { + "type": "String" } }, "triggers": { @@ -188,7 +225,7 @@ "inputs": { "name": "transformed_rf_api_credential_dump_creds", "value": { - "email": "@items('For_Each_-_Make_new_and_known_Credential_dumps_be_comparable')" + "email": "@string(items('For_Each_-_Make_new_and_known_Credential_dumps_be_comparable'))" } } } @@ -286,7 +323,10 @@ "type": "AppendToArrayVariable", "inputs": { "name": "unknown_malware_log_creds", - "value": "@items('For_Each_new_Malware_log_exposures')" + "value": { + "login": "@items('For_Each_new_Malware_log_exposures')?['login']", + "domain": "@items('For_Each_new_Malware_log_exposures')?['domain']" + } } } }, @@ -528,7 +568,7 @@ }, "type": "ApiConnection", "inputs": { - "body": "@{parameters('credential_dumps_log_analytics_custom_log_name')}\n| project email=email_s", + "body": "@{parameters('credential_dumps_log_analytics_custom_log_name')}\n| project email=email", "host": { "connection": { "name": "@parameters('$connections')['azuremonitorlogs']['connectionId']" @@ -553,7 +593,7 @@ }, "type": "ApiConnection", "inputs": { - "body": "@{parameters('malware_logs_log_analytics_custom_log_name')}\n| project login=login_s, domain=domain_s", + "body": "@{parameters('malware_logs_log_analytics_custom_log_name')}\n| project login=login, domain=domain", "host": { "connection": { "name": "@parameters('$connections')['azuremonitorlogs']['connectionId']" @@ -576,20 +616,18 @@ "Succeeded" ] }, - "type": "ApiConnection", + "type": "Http", "inputs": { - "body": "@{outputs('Transform_new_Credential_dump_exposures_array_into_a_JSON_object')}", + "method": "POST", + "uri": "@{parameters('DceEndpoint')}/dataCollectionRules/@{parameters('DcrCredentialDumpsImmutableId')}/streams/@{parameters('StreamCredentialDumpsName')}?api-version=2023-01-01", "headers": { - "Log-Type": "@parameters('credential_dumps_log_analytics_custom_log_name')", - "time-generated-field": "@{utcNow()}" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['azureloganalyticsdatacollector']['connectionId']" - } + "Content-Type": "application/json" }, - "method": "post", - "path": "/api/logs" + "body": "@outputs('Transform_new_Credential_dump_exposures_array_into_a_JSON_object')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://monitor.azure.com" + } } }, "Send_Data_-_Save_new_Malware_log_exposures_into_Log_Analytics_Custom_Log": { @@ -598,20 +636,18 @@ "Succeeded" ] }, - "type": "ApiConnection", + "type": "Http", "inputs": { - "body": "@{outputs('Transform_new_Malware_log_exposures_array_into_a_JSON_object')}", + "method": "POST", + "uri": "@{parameters('DceEndpoint')}/dataCollectionRules/@{parameters('DcrMalwareLogsImmutableId')}/streams/@{parameters('StreamMalwareLogsName')}?api-version=2023-01-01", "headers": { - "Log-Type": "@parameters('malware_logs_log_analytics_custom_log_name')", - "time-generated-field": "@{utcNow()}" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['azureloganalyticsdatacollector']['connectionId']" - } + "Content-Type": "application/json" }, - "method": "post", - "path": "/api/logs" + "body": "@outputs('Transform_new_Malware_log_exposures_array_into_a_JSON_object')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://monitor.azure.com" + } } }, "Transform_new_Credential_dump_exposures_array_into_a_JSON_object": { @@ -638,11 +674,6 @@ "parameters": { "$connections": { "value": { - "azureloganalyticsdatacollector": { - "connectionId": "[resourceId('Microsoft.Web/connections', variables('LogAnalyticsDataCollectorConnectionName'))]", - "connectionName": "[variables('LogAnalyticsDataCollectorConnectionName')]", - "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/azureloganalyticsdatacollector')]" - }, "azuremonitorlogs": { "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureMonitorLogsConnectionName'))]", "connectionName": "[variables('AzureMonitorLogsConnectionName')]", @@ -654,22 +685,25 @@ "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/recordedfutureidenti')]" } } + }, + "DceEndpoint": { + "value": "[reference(variables('dceResourceId'), '2024-03-11').logsIngestion.endpoint]" + }, + "DcrMalwareLogsImmutableId": { + "value": "[reference(variables('dcrMalwareLogsResourceId'), '2024-03-11').immutableId]" + }, + "DcrCredentialDumpsImmutableId": { + "value": "[reference(variables('dcrCredentialDumpsResourceId'), '2024-03-11').immutableId]" + }, + "StreamMalwareLogsName": { + "value": "[variables('StreamMalwareLogsName')]" + }, + "StreamCredentialDumpsName": { + "value": "[variables('StreamCredentialDumpsName')]" } } } }, - { - "type": "Microsoft.Web/connections", - "apiVersion": "2018-07-01-preview", - "name": "[variables('LogAnalyticsDataCollectorConnectionName')]", - "location": "[resourceGroup().location]", - "properties": { - "api": { - "id": "[concat('/subscriptions/',subscription().subscriptionId,'/providers/Microsoft.Web/locations/',resourceGroup().location,'/managedApis/azureloganalyticsdatacollector')]" - }, - "displayName": "[variables('LogAnalyticsDataCollectorConnectionName')]" - } - }, { "type": "Microsoft.Web/connections", "apiVersion": "2018-07-01-preview", @@ -693,6 +727,36 @@ }, "displayName": "[variables('RecordedFutureIdentityConnectionName')]" } - } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(variables('dcrMalwareLogsResourceId'), parameters('PlaybookName'), variables('MonitoringMetricsPublisherRoleId'))]", + "scope": "[variables('dcrMalwareLogsResourceId')]", + "dependsOn": [ + "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + ], + "properties": { + "roleDefinitionId": "[variables('MonitoringMetricsPublisherRoleId')]", + "principalId": "[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "condition": "[parameters('create_role_assignment')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(variables('dcrCredentialDumpsResourceId'), parameters('PlaybookName'), variables('MonitoringMetricsPublisherRoleId'))]", + "scope": "[variables('dcrCredentialDumpsResourceId')]", + "dependsOn": [ + "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + ], + "properties": { + "roleDefinitionId": "[variables('MonitoringMetricsPublisherRoleId')]", + "principalId": "[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "condition": "[parameters('create_role_assignment')]" + } ] } \ No newline at end of file diff --git a/Solutions/Recorded Future Identity/Playbooks/v3.0/readme.md b/Solutions/Recorded Future Identity/Playbooks/v3.0/readme.md index 0d576d63c39..4c68638f19b 100644 --- a/Solutions/Recorded Future Identity/Playbooks/v3.0/readme.md +++ b/Solutions/Recorded Future Identity/Playbooks/v3.0/readme.md @@ -2,6 +2,13 @@ ### This is a readme for the old version of Recorded Future Identity, for the new version based on Playbook Alerts, see [readme](../readme.md) +> [!IMPORTANT] +> ### Log Ingestion API migration (deadline: 2026-09-14) +> +> These playbooks previously used the deprecated Azure Log Analytics Data Collector connector, which Microsoft is retiring on September 14, 2026. They have been updated to use the Log Ingestion API via a Data Collection Endpoint (DCE) and Data Collection Rules (DCRs). +> +> **If you have existing deployments**, you must redeploy the Data Connectors infrastructure (step 1 below) and each affected playbook (`RFI-lookup-and-save-user`, `RFI-search-external-user`, `RFI-search-workforce-user`) using the updated templates before the deadline. + ## Table of Contents 1. [Overview](#overview) @@ -71,20 +78,46 @@ Recorded Future recommend deploying playbooks in this solution from this README, ### Prerequisites - A Microsoft EntraID Tenant and subscription. -- Azure subscription Owner or Contributor permissions so you can install the Logic Apps. [Azure roles - Classic subscription administrator roles, Azure roles, and Entra ID roles](https://docs.microsoft.com/azure/role-based-access-control/rbac-and-directory-admin-roles#azure-roles). -- A [Log Analytics workspace](https://docs.microsoft.com/azure/azure-monitor/essentials/resource-logs#send-to-log-analytics-workspace). If you don't have a workspace, learn [how to create a Log Analytics workspace](https://docs.microsoft.com/azure/azure-monitor/logs/quick-create-workspace). Note that the custom logs specified as parameters in these logic apps will be created automatically if they don’t already exist. Note the name of the Log Analytic Workspace, it will be used at a later stage of the deployment. -- In Consumption logic apps, before you can create or manage logic apps and their connections, you need specific permissions. For more information about these permissions, review [Secure operations - Secure access and data in Azure Logic Apps](https://docs.microsoft.com/azure/logic-apps/logic-apps-securing-a-logic-app#secure-operations). +- A [Log Analytics workspace](https://docs.microsoft.com/azure/azure-monitor/essentials/resource-logs#send-to-log-analytics-workspace). Note its name — it is required during deployment. If you don't have one, learn [how to create a Log Analytics workspace](https://docs.microsoft.com/azure/azure-monitor/logs/quick-create-workspace). - For `Recorded Future Identity` Connections you will need `Recorded Future Identity API` token. To obtain one - check out [this section](#how_to_obtain_Recorded_Future_API_token). +- In Consumption logic apps, before you can create or manage logic apps and their connections, you need specific permissions. For more information about these permissions, review [Secure operations - Secure access and data in Azure Logic Apps](https://docs.microsoft.com/azure/logic-apps/logic-apps-securing-a-logic-app#secure-operations). + +#### Required permissions (resource group scope) + +| Deployment step | Required roles | +|-|-| +| Step 1 — Data Connectors infrastructure | [Monitoring Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#monitoring-contributor) + [Log Analytics Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#log-analytics-contributor) | +| Steps 2–6 — Connector and playbooks (with auto role assignment) | [Owner](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#owner) or [Logic App Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#logic-app-contributor) + [Role Based Access Control Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator) | +| Steps 2–6 — Connector and playbooks (manual role assignment) | [Logic App Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#logic-app-contributor) — then ask an admin to assign [Monitoring Metrics Publisher](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/monitor#monitoring-metrics-publisher) on the relevant DCRs to the Logic App's managed identity | ## Playbooks > [!IMPORTANT] -> Deploy connector and base playbooks before deploying the Search playbooks. +> Deploy the Data Connectors infrastructure (step 1) and connector (step 2) before deploying the base and search playbooks. + +### Step 1 — Deploy Data Connectors infrastructure + +Deploys a shared Data Collection Endpoint (DCE) and three Data Collection Rules (DCRs) — one each for lookup results, malware logs, and credential dumps — along with the corresponding Log Analytics tables. Deploy this into the same resource group as your Log Analytics Workspace. + +![Deploy to Azure](https://aka.ms/deploytoazurebutton) +![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton) + +
+Expand deployment parameters: + +| Parameter | Description | +|-|-| +| **log_analytics_workspace_name** | Name of your Log Analytics Workspace. Must be in the same resource group. | +| **log_analytics_workspace_location** | Location of the workspace. Defaults to the resource group location. | + +
+
-### Connector-playbooks + +### Step 2 — Connector playbooks Connector playbooks are used by other playbooks in this solution to communicate with Recorded Future backend API. @@ -220,10 +253,10 @@ Expand Playbook Workflow | Parameter | Description | |-|-| -| **Subscription** | Your Azure Subscription to deploy the Solution in. All resources in an Azure subscription are billed together. | -| **Resource group** | Resource group in your Subscription to deploy the Solution in. A resource group is a collection of resources that share the same lifecycle, permissions, and policies. | -| **Region** | Choose the Azure region that's right for you and your customers. Not every resource is available in every region. | -| **Playbook-Name** | Playbook name to use for this playbook (ex. "RFI-lookup-and-save-user"). | +| **Playbook-Name** | Playbook name to use for this playbook (default: `RFI-lookup-and-save-user`). | +| **log_analytics_workspace_name** | Name of your Log Analytics Workspace. Used to resolve the DCE and DCR deployed in step 1. | +| **create_role_assignment** | Whether to automatically assign the _Monitoring Metrics Publisher_ role on the DCR to the Logic App's managed identity. See [Required Permissions](#required-permissions) for details. | +
@@ -258,13 +291,12 @@ External search playbook - will get data from Recorded Future on your clients le | Parameter | Description | |-|-| -| **Subscription** | Your Azure Subscription to deploy the Solution in. All resources in an Azure subscription are billed together. | -| **Resource group** | Resource group in your Subscription to deploy the Solution in. A resource group is a collection of resources that share the same lifecycle, permissions, and policies. | -| **Region** | Choose the Azure region that's right for you and your customers. Not every resource is available in every region. | -| **Playbook-Name** | Playbook name to use for this playbook (ex. "RFI-search-workforce-user"). | -| **Playbook-Name-add-EntraID-security-group-user** | Playbook name to use for "RFI-add-EntraID-security-group-user" playbook. | -| **Playbook-Name-confirm-EntraID-risky-user** | Playbook name to use for "RFI-confirm-EntraID-risky-user" playbook. | -| **Playbook-Name-lookup-and-save-user** | Playbook name to use for "RFI-lookup-and-save-user" playbook. | +| **Playbook-Name** | Playbook name to use for this playbook (default: `RFI-search-workforce-user`). | +| **Playbook-Name-add-EntraID-security-group-user** | Name of the `RFI-add-EntraID-security-group-user` playbook. | +| **Playbook-Name-confirm-EntraID-risky-user** | Name of the `RFI-confirm-EntraID-risky-user` playbook. | +| **Playbook-Name-lookup-and-save-user** | Name of the `RFI-lookup-and-save-user` playbook. | +| **create_role_assignment** | Whether to automatically assign the _Monitoring Metrics Publisher_ role on the DCRs to the Logic App's managed identity. See [Required Permissions](#required-permissions) for details. | +
@@ -279,13 +311,12 @@ External search playbook - will get data from Recorded Future on your clients le | Parameter | Description | |-|-| -| **Subscription** | Your Azure Subscription to deploy the Solution in. All resources in an Azure subscription are billed together. | -| **Resource group** | Resource group in your Subscription to deploy the Solution in. A resource group is a collection of resources that share the same lifecycle, permissions, and policies. | -| **Region** | Choose the Azure region that's right for you and your customers. Not every resource is available in every region. | -| **Playbook-Name** | Playbook name to use for this playbook (ex. "RFI-search-external-user"). | -| **Playbook-Name-add-EntraID-security-group-user** | Playbook name to use for "RFI-add-EntraID-security-group-user" playbook. | -| **Playbook-Name-confirm-EntraID-risky-user** | Playbook name to use for "RFI-confirm-EntraID-risky-user" playbook. | -| **Playbook-Name-lookup-and-save-user** | Playbook name to use for "RFI-lookup-and-save-user" playbook. | +| **Playbook-Name** | Playbook name to use for this playbook (default: `RFI-search-external-user`). | +| **Playbook-Name-add-EntraID-security-group-user** | Name of the `RFI-add-EntraID-security-group-user` playbook. | +| **Playbook-Name-confirm-EntraID-risky-user** | Name of the `RFI-confirm-EntraID-risky-user` playbook. | +| **Playbook-Name-lookup-and-save-user** | Name of the `RFI-lookup-and-save-user` playbook. | +| **create_role_assignment** | Whether to automatically assign the _Monitoring Metrics Publisher_ role on the DCR to the Logic App's managed identity. See [Required Permissions](#required-permissions) for details. | +
@@ -311,7 +342,6 @@ The Recorded Future identity solution uses the following connectors. Information |-|-| | **/recordedfutureidenti** | [Microsoft power platform connector](https://learn.microsoft.com/en-us/connectors/recordedfutureidenti/).
[How to obtain Recorded Future API token](#how_to_obtain_Recorded_Future_API_token) | | **/RFI-CustomConnector** | [RecordedFuture-CustomConnector](../../../Recorded%20Future/Playbooks/Connectors/RecordedFuture-CustomConnector/readme.md)
Same API token as the recordedfutureidenti connector. | -| **/azureloganalyticsdatacollector** | [Azure Log Analytics Data Collector](https://learn.microsoft.com/en-us/connectors/azureloganalyticsdatacollector/)
[How to find Log Analytics Workspace key.](https://learn.microsoft.com/en-us/answers/questions/1154380/where-is-azure-is-the-primary-key-and-workspace-id) | **/azuremonitorlogs** | [Azure Monitor Logs](https://learn.microsoft.com/en-us/connectors/azuremonitorlogs/) | | **/azuread** | [Microsoft Entra ID power platform connectors](https://learn.microsoft.com/en-us/connectors/azuread/). | | **/azureadip** | [Azure AD Identity Protection](https://learn.microsoft.com/en-us/connectors/azureadip/) | From 3f2325e1b912059f2a18a0dbfb8cca02bbc4b579 Mon Sep 17 00:00:00 2001 From: Niklas Logren Date: Tue, 16 Jun 2026 12:12:39 +0200 Subject: [PATCH 2/4] docs: remove 'via the Azure Monitor Logs Ingestion API' from connector descriptions --- .../Data Connectors/azuredeploy-alert-importer.json | 2 +- .../Data Connectors/azuredeploy-v3.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json index 0f6ceaa7d61..67794083705 100644 --- a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json +++ b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json @@ -211,7 +211,7 @@ "id": "RecordedFutureIdentityAlertImporter", "title": "Recorded Future Identity - Playbook Alert Importer", "publisher": "Recorded Future", - "descriptionMarkdown": "Imports Recorded Future Identity Playbook Alerts into Microsoft Sentinel via the Azure Monitor Logs Ingestion API. Enables incident creation via Analytic Rules on the `RFI_PlaybookAlertResults_V2_CL` table.", + "descriptionMarkdown": "Imports Recorded Future Identity Playbook Alerts into Microsoft Sentinel. Enables incident creation via Analytic Rules on the `RFI_PlaybookAlertResults_V2_CL` table.", "graphQueries": [ { "metricName": "Playbook Alert Results", diff --git a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json index 10383b3ec1f..7642aa5b6f9 100644 --- a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json +++ b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json @@ -303,7 +303,7 @@ "id": "RecordedFutureIdentityV3", "title": "Recorded Future Identity - Credential Exposure Importer", "publisher": "Recorded Future", - "descriptionMarkdown": "Imports Recorded Future Identity credential exposure data into Microsoft Sentinel via the Azure Monitor Logs Ingestion API. Writes malware log exposures to `RFI_MalwareLogs_V2_CL`, credential dump exposures to `RFI_CredentialDumps_V2_CL`, and user lookup results to `RFI_UsersLookupResults_V2_CL`.", + "descriptionMarkdown": "Imports Recorded Future Identity credential exposure data into Microsoft Sentinel. Writes malware log exposures to `RFI_MalwareLogs_V2_CL`, credential dump exposures to `RFI_CredentialDumps_V2_CL`, and user lookup results to `RFI_UsersLookupResults_V2_CL`.", "graphQueries": [ { "metricName": "Malware Log Exposures", From 66ba3df1c01026f99f486b0597632704db01d64e Mon Sep 17 00:00:00 2001 From: Niklas Logren Date: Tue, 16 Jun 2026 12:30:33 +0200 Subject: [PATCH 3/4] feat: update connector tiles to match readme deployment steps - Add readme links to descriptionMarkdown on both tiles - Add Step 2 (RFI-CustomConnector) to both tiles - Alert importer: add Step 4 (Analytics Rule), fix permissions text - v3.0: Step 3 links to readme instead of listing playbooks inline --- .../azuredeploy-alert-importer.json | 18 +++++++++++++----- .../Data Connectors/azuredeploy-v3.json | 12 ++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json index 67794083705..7b6988d6cab 100644 --- a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json +++ b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-alert-importer.json @@ -211,7 +211,7 @@ "id": "RecordedFutureIdentityAlertImporter", "title": "Recorded Future Identity - Playbook Alert Importer", "publisher": "Recorded Future", - "descriptionMarkdown": "Imports Recorded Future Identity Playbook Alerts into Microsoft Sentinel. Enables incident creation via Analytic Rules on the `RFI_PlaybookAlertResults_V2_CL` table.", + "descriptionMarkdown": "Imports Recorded Future Identity Playbook Alerts into Microsoft Sentinel. Enables incident creation via Analytic Rules on the `RFI_PlaybookAlertResults_V2_CL` table. For full installation details, see the [readme](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Recorded%20Future%20Identity/Playbooks/readme.md).", "graphQueries": [ { "metricName": "Playbook Alert Results", @@ -266,7 +266,7 @@ "customs": [ { "name": "Azure Subscription", - "description": "Contributor permissions required to deploy ARM templates (Data Connectors infrastructure and playbook)." + "description": "Step 1 (Data Connectors infrastructure) requires **Monitoring Contributor** and **Log Analytics Contributor** on the resource group. Step 3 (playbook) requires **Owner** or **Role Based Access Control Administrator** when deploying with automatic role assignment (`create_role_assignment=true`), otherwise **Contributor** suffices." }, { "name": "Recorded Future API Token", @@ -277,11 +277,19 @@ "instructionSteps": [ { "title": "Step 1 \u2014 Deploy Data Connectors infrastructure", - "description": "Deploys the shared Data Collection Endpoint (DCE), Data Collection Rule (DCR), Log Analytics table (`RFI_PlaybookAlertResults_V2_CL`), and this connector definition tile.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Frecordedfuture%2FAzure-Sentinel%2FRFPD-77178-log-ingestion-api%2FSolutions%2FRecorded%20Future%20Identity%2FData%20Connectors%2Fazuredeploy-alert-importer.json)" + "description": "Deploys the shared Data Collection Endpoint (DCE), Data Collection Rule (DCR), Log Analytics table (`RFI_PlaybookAlertResults_V2_CL`), and this connector definition tile.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Data%2520Connectors/azuredeploy-alert-importer.json)" }, { - "title": "Step 2 \u2014 Deploy the RFI-Playbook-Alert-Importer-LAW playbook", - "description": "Deploys the Logic App that imports Recorded Future Identity Playbook Alerts and writes them to the Log Analytics table via the Logs Ingestion API using Managed Identity.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Frecordedfuture%2FAzure-Sentinel%2FRFPD-77178-log-ingestion-api%2FSolutions%2FRecorded%20Future%20Identity%2FPlaybooks%2FRFI-Playbook-Alert-Importer-LAW%2Fazuredeploy.json)\n\nAfter deployment, open the Logic App and authorize the different connectors, then enable the Logic App." + "title": "Step 2 \u2014 Deploy RFI-CustomConnector", + "description": "The custom connector handles authentication towards the Recorded Future API. Deploy it once \u2014 it is shared by the playbook.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Playbooks/Connectors/RFI-CustomConnector-0-2-0/azuredeploy.json)" + }, + { + "title": "Step 3 \u2014 Deploy RFI-Playbook-Alert-Importer-LAW", + "description": "Deploys the Logic App that imports Recorded Future Identity Playbook Alerts and writes them to the Log Analytics table using Managed Identity. Set `create_role_assignment=false` if your organization requires manual role assignment (see permissions above). After deployment, authorize the connectors and enable the Logic App.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Playbooks/RFI-Playbook-Alert-Importer-LAW/azuredeploy.json)" + }, + { + "title": "Step 4 \u2014 Deploy Analytics Rule", + "description": "Creates Microsoft Sentinel incidents from the `RFI_PlaybookAlertResults_V2_CL` table. The rule is also available under **Microsoft Sentinel \u2192 Configuration \u2192 Analytics \u2192 Rule Templates**.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Analytic%2520Rules/IncidentCreation/azuredeploy.json)" } ] } diff --git a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json index 7642aa5b6f9..a00dc835d43 100644 --- a/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json +++ b/Solutions/Recorded Future Identity/Data Connectors/azuredeploy-v3.json @@ -303,7 +303,7 @@ "id": "RecordedFutureIdentityV3", "title": "Recorded Future Identity - Credential Exposure Importer", "publisher": "Recorded Future", - "descriptionMarkdown": "Imports Recorded Future Identity credential exposure data into Microsoft Sentinel. Writes malware log exposures to `RFI_MalwareLogs_V2_CL`, credential dump exposures to `RFI_CredentialDumps_V2_CL`, and user lookup results to `RFI_UsersLookupResults_V2_CL`.", + "descriptionMarkdown": "Imports Recorded Future Identity credential exposure data into Microsoft Sentinel. Writes malware log exposures to `RFI_MalwareLogs_V2_CL`, credential dump exposures to `RFI_CredentialDumps_V2_CL`, and user lookup results to `RFI_UsersLookupResults_V2_CL`. For full installation details, see the [readme](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Recorded%20Future%20Identity/Playbooks/v3.0/readme.md).", "graphQueries": [ { "metricName": "Malware Log Exposures", @@ -387,11 +387,15 @@ "instructionSteps": [ { "title": "Step 1 \u2014 Deploy Data Connectors infrastructure", - "description": "Deploys the shared Data Collection Endpoint (DCE), Data Collection Rules (DCRs), Log Analytics tables, and this connector definition tile.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FSolutions%2FRecorded%20Future%20Identity%2FData%20Connectors%2Fazuredeploy-v3.json)" + "description": "Deploys the shared Data Collection Endpoint (DCE), Data Collection Rules (DCRs), Log Analytics tables (`RFI_MalwareLogs_V2_CL`, `RFI_CredentialDumps_V2_CL`, `RFI_UsersLookupResults_V2_CL`), and this connector definition tile. Deploy this into the same resource group as your Log Analytics Workspace.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Data%2520Connectors/azuredeploy-v3.json)" }, { - "title": "Step 2 \u2014 Deploy the playbooks", - "description": "Deploy one or more of the following playbooks. See the [v3.0 readme](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Recorded%20Future%20Identity/Playbooks/v3.0/readme.md) for full deployment instructions.\n\n- `RFI-search-workforce-user` \u2014 searches for compromised workforce users\n- `RFI-search-external-user` \u2014 searches for compromised external/customer users\n- `RFI-lookup-and-save-user` \u2014 fetches detailed lookup data for a specific user" + "title": "Step 2 \u2014 Deploy RFI-CustomConnector", + "description": "The custom connector handles authentication towards the Recorded Future API. Deploy it once \u2014 it is shared by the playbooks.\n\n[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Recorded%2520Future%2520Identity/Playbooks/v3.0/RFI-CustomConnector-0-1-0/azuredeploy.json)" + }, + { + "title": "Step 3 \u2014 Deploy playbooks", + "description": "Deploy one or more of the search/lookup playbooks depending on your use case. See the [readme](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Recorded%20Future%20Identity/Playbooks/v3.0/readme.md) for full deployment instructions, parameters, and post-deployment configuration." } ] } From daeed6728060c6e6a504ff6d3708a34939f6358d Mon Sep 17 00:00:00 2001 From: Niklas Logren Date: Tue, 16 Jun 2026 12:57:57 +0200 Subject: [PATCH 4/4] fix: add requiredDataConnectors to analytic rule YAML --- .../IncidentCreation/RecordedFutureIdentityExposure.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Solutions/Recorded Future Identity/Analytic Rules/IncidentCreation/RecordedFutureIdentityExposure.yaml b/Solutions/Recorded Future Identity/Analytic Rules/IncidentCreation/RecordedFutureIdentityExposure.yaml index 1351a9da71a..281cbe86671 100644 --- a/Solutions/Recorded Future Identity/Analytic Rules/IncidentCreation/RecordedFutureIdentityExposure.yaml +++ b/Solutions/Recorded Future Identity/Analytic Rules/IncidentCreation/RecordedFutureIdentityExposure.yaml @@ -11,6 +11,10 @@ triggerThreshold: 0 tactics: - CredentialAccess relevantTechniques: [] +requiredDataConnectors: + - connectorId: RecordedFutureIdentityAlertImporter + dataTypes: + - RFI_PlaybookAlertResults_V2_CL query: | RFI_PlaybookAlertResults_V2_CL | where TimeGenerated >= now(-15m)