{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/3192085a-e97e-440f-acb7-9227622949a4')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/3192085a-e97e-440f-acb7-9227622949a4')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "{{SynqlyAlertDescription}}",
          "alertDisplayNameFormat": "{{SynqlyAlertName}}",
          "alertDynamicProperties": [
            {
              "alertProperty": "Techniques",
              "value": "AllAttackTechniques"
            },
            {
              "alertProperty": "ConfidenceLevel",
              "value": "SynqlyConfidenceLevel"
            },
            {
              "alertProperty": "ConfidenceScore",
              "value": "SynqlyConfidenceScore"
            },
            {
              "alertProperty": "RemediationSteps",
              "value": "SynqlyRemediation"
            },
            {
              "alertProperty": "AlertLink",
              "value": "SynqlyAlertLink"
            }
          ],
          "alertSeverityColumnName": "SynqlyAlertSeverity",
          "alertTacticsColumnName": "SynqlyTactics"
        },
        "alertRuleTemplateName": "3192085a-e97e-440f-acb7-9227622949a4",
        "customDetails": {
          "AttackTechniques": "AllAttackTechniques",
          "DestinationDomain": "DestinationDomain",
          "DestinationIp": "DestinationIp",
          "DeviceIp": "DeviceIp",
          "FilePath": "FileValue",
          "MalwareName": "MalwareName",
          "OriginalSeverity": "OriginalSeverity",
          "PrimaryIpRole": "PrimaryIpRole",
          "ProductVersion": "ProductVersion",
          "SourceAlertId": "SynqlyAlertId",
          "SourceIp": "SourceIp",
          "SourceProduct": "SourceProduct",
          "SourceRuleId": "SourceRuleId",
          "SourceUrl": "EntityUrl",
          "SourceVendor": "SourceVendor"
        },
        "description": "Creates one Microsoft Sentinel alert for each qualifying Synqly-attributed ASIM Alert Event row. Native ASIM values take precedence, with retained OCSF data used to recover investigation context when normalized fields are empty. Replayed source rows can create additional alerts, and Microsoft Sentinel or Defender XDR may correlate related alerts into a shared incident.\n",
        "displayName": "Synqly Alert Event",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountSid",
                "identifier": "Sid"
              },
              {
                "columnName": "AccountDomain",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "HostNameValue",
                "identifier": "HostName"
              },
              {
                "columnName": "HostDomainValue",
                "identifier": "DnsDomain"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "PrimaryIp",
                "identifier": "Address"
              }
            ]
          },
          {
            "entityType": "FileHash",
            "fieldMappings": [
              {
                "columnName": "FileHashAlgorithm",
                "identifier": "Algorithm"
              },
              {
                "columnName": "FileHashValue",
                "identifier": "Value"
              }
            ]
          },
          {
            "entityType": "Process",
            "fieldMappings": [
              {
                "columnName": "ProcessIdValue",
                "identifier": "ProcessId"
              },
              {
                "columnName": "ProcessCommandLineValue",
                "identifier": "CommandLine"
              }
            ]
          }
        ],
        "eventGroupingSettings": {
          "aggregationKind": "AlertPerResult"
        },
        "incidentConfiguration": {
          "createIncident": true,
          "groupingConfiguration": {
            "enabled": false,
            "lookbackDuration": "PT5M",
            "matchingMethod": "AllEntities",
            "reopenClosedIncident": false
          }
        },
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SynqlyIntegrationConnector/Analytic%20Rules/SynqlyAlertEventToSentinelAlert.yaml",
        "query": "ASimAlertEventLogs\n| where tostring(AdditionalFields._ConnectorId) == \"SynqlySentinelASIMConnector\"\n| extend OCSF = coalesce(todynamic(AdditionalFields.OCSF), dynamic({}))\n| mv-apply Evidence = array_concat(coalesce(todynamic(OCSF.evidences), dynamic([])), dynamic([{}])) on (\n    mv-apply EvidenceHash = array_concat(coalesce(todynamic(Evidence.data.file.hashes), dynamic([])), coalesce(todynamic(Evidence.data.process.hashes), dynamic([])), dynamic([{}])) on (\n        summarize\n            OcsfSHA256 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"SHA256\"),\n            OcsfSHA1 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"SHA1\"),\n            OcsfMD5 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"MD5\")\n    )\n    | summarize\n        OcsfUserName = take_anyif(tostring(Evidence.data.process.user.name), isnotempty(tostring(Evidence.data.process.user.name))),\n        OcsfUserSid = take_anyif(tostring(Evidence.data.process.user.uid), isnotempty(tostring(Evidence.data.process.user.uid))),\n        OcsfUserDomain = take_anyif(tostring(Evidence.data.process.user.domain), isnotempty(tostring(Evidence.data.process.user.domain))),\n        OcsfProcessId = take_anyif(tostring(Evidence.data.process.pid), isnotempty(tostring(Evidence.data.process.pid))),\n        OcsfProcessCommandLine = take_anyif(tostring(Evidence.data.process.cmd_line), isnotempty(tostring(Evidence.data.process.cmd_line))),\n        OcsfFileName = take_anyif(tostring(Evidence.data.file.name), isnotempty(tostring(Evidence.data.file.name))),\n        OcsfFilePath = take_anyif(tostring(Evidence.data.file.path), isnotempty(tostring(Evidence.data.file.path))),\n        OcsfSourceHost = take_anyif(tostring(Evidence.data.network.src_endpoint.host.hostname), isnotempty(tostring(Evidence.data.network.src_endpoint.host.hostname))),\n        OcsfSourceIp = take_anyif(tostring(Evidence.data.network.src_endpoint.ip), isnotempty(tostring(Evidence.data.network.src_endpoint.ip))),\n        OcsfDestinationIp = take_anyif(tostring(Evidence.data.network.dst_endpoint.ip), isnotempty(tostring(Evidence.data.network.dst_endpoint.ip))),\n        OcsfDestinationDomain = take_anyif(tostring(Evidence.data.network.dst_endpoint.domain), isnotempty(tostring(Evidence.data.network.dst_endpoint.domain))),\n        OcsfSHA256 = take_anyif(OcsfSHA256, isnotempty(OcsfSHA256)),\n        OcsfSHA1 = take_anyif(OcsfSHA1, isnotempty(OcsfSHA1)),\n        OcsfMD5 = take_anyif(OcsfMD5, isnotempty(OcsfMD5))\n)\n| mv-apply Attack = array_concat(coalesce(todynamic(OCSF.attacks), dynamic([])), dynamic([{}])) on (\n    extend TacticId = tostring(Attack.tactic.uid), TechniqueId = tostring(Attack.technique.uid), SubTechniqueId = tostring(Attack.sub_technique.uid)\n    | extend TacticName = case(\n        TacticId == \"TA0043\", \"Reconnaissance\", TacticId == \"TA0042\", \"ResourceDevelopment\",\n        TacticId == \"TA0001\", \"InitialAccess\", TacticId == \"TA0002\", \"Execution\",\n        TacticId == \"TA0003\", \"Persistence\", TacticId == \"TA0004\", \"PrivilegeEscalation\",\n        TacticId == \"TA0005\", \"DefenseEvasion\", TacticId == \"TA0006\", \"CredentialAccess\",\n        TacticId == \"TA0007\", \"Discovery\", TacticId == \"TA0008\", \"LateralMovement\",\n        TacticId == \"TA0009\", \"Collection\", TacticId == \"TA0010\", \"Exfiltration\",\n        TacticId == \"TA0011\", \"CommandAndControl\", TacticId == \"TA0040\", \"Impact\", \"\")\n    | summarize\n        OcsfTactics = make_set_if(TacticName, isnotempty(TacticName), 14),\n        OcsfTechniques = make_set_if(TechniqueId, isnotempty(TechniqueId), 50),\n        OcsfSubTechniques = make_set_if(SubTechniqueId, isnotempty(SubTechniqueId), 50)\n)\n| extend\n    ActorUser = todynamic(OCSF.actor.process.user),\n    Malware = todynamic(OCSF.malware[0]),\n    NativeConfidenceValue = toreal(ThreatConfidence),\n    OcsfConfidenceValue = toreal(OCSF.confidence_score),\n    NativeConfidenceLabel = tolower(trim(\" \", tostring(ThreatOriginalConfidence))),\n    OcsfConfidenceLabel = tolower(trim(\" \", tostring(OCSF.confidence))),\n    NativeSourceId = coalesce(tostring(EventUid), tostring(AlertId), tostring(EventOriginalUid)),\n    OcsfSourceId = coalesce(tostring(OCSF.finding_info.uid), tostring(OCSF.uid)),\n    SourceIdMaterial = tostring(pack_array(tostring(OCSF.class_uid), tostring(OCSF.type_uid), tostring(OCSF.activity_id), tostring(EventStartTime), tostring(EventEndTime), tostring(EventVendor), tostring(EventProduct), tostring(EventMessage), tostring(OCSF[\"time\"])))\n| extend\n    SynqlyAlertId = coalesce(NativeSourceId, OcsfSourceId, strcat(\"generated-\", hash_sha256(SourceIdMaterial))),\n    SynqlyAlertName = coalesce(tostring(AlertName), tostring(OCSF.finding_info[\"title\"]), tostring(OCSF.class_name), \"Synqly normalized alert event\"),\n    SynqlyAlertDescription = coalesce(tostring(EventMessage), tostring(AlertDescription), tostring(OCSF.message), \"Synqly normalized alert event\"),\n    SeverityValue = tolower(coalesce(tostring(EventSeverity), tostring(EventOriginalSeverity), tostring(OCSF.severity))),\n    SynqlyTactics = coalesce(tostring(AttackTactics), strcat_array(OcsfTactics, \",\")),\n    AllAttackTechniques = coalesce(tostring(AttackTechniques), strcat_array(array_concat(OcsfTechniques, OcsfSubTechniques), \",\")),\n    SynqlyConfidenceScore = coalesce(\n        iff(NativeConfidenceValue between (0.0 .. 100.0), NativeConfidenceValue / 100.0, real(null)),\n        iff(OcsfConfidenceValue between (0.0 .. 100.0), OcsfConfidenceValue / 100.0, real(null))),\n    SynqlyConfidenceLevel = case(\n        NativeConfidenceLabel == \"high\", \"High\",\n        NativeConfidenceLabel == \"low\", \"Low\",\n        NativeConfidenceLabel == \"unknown\", \"Unknown\",\n        isnotempty(NativeConfidenceLabel), \"\",\n        OcsfConfidenceLabel == \"high\", \"High\",\n        OcsfConfidenceLabel == \"low\", \"Low\",\n        OcsfConfidenceLabel == \"unknown\", \"Unknown\",\n        \"\"),\n    SynqlyRemediation = coalesce(tostring(AttackRemediationSteps), strcat_array(todynamic(OCSF.remediation.kb_articles), \"; \")),\n    SynqlyAlertLink = coalesce(tostring(EventReportUrl), tostring(OCSF.finding_info.src_url)),\n    SourceVendor = coalesce(tostring(EventVendor), tostring(OCSF.metadata.product.vendor_name)),\n    SourceProduct = coalesce(tostring(EventProduct), tostring(OCSF.metadata.product.name)),\n    ProductVersion = coalesce(tostring(EventProductVersion), tostring(OCSF.metadata.product.version)),\n    OriginalSeverity = coalesce(tostring(EventOriginalSeverity), tostring(OCSF.severity)),\n    SourceRuleId = coalesce(\n        tostring(Rule),\n        tostring(OCSF.finding_info.analytic.uid),\n        iff(isnotempty(tostring(RuleName)) and not(tostring(RuleName) matches regex @\"^\\d+$\"), tostring(RuleName), \"\")),\n    AccountName = coalesce(tostring(Username), tostring(User), tostring(ActorUser.name), OcsfUserName),\n    AccountSid = coalesce(iff(tolower(tostring(UserIdType)) contains \"sid\", tostring(UserId), \"\"), tostring(ActorUser.uid), OcsfUserSid),\n    AccountDomain = coalesce(tostring(UserScope), tostring(ActorUser.domain), OcsfUserDomain),\n    NativeDeviceHost = coalesce(tostring(DvcHostname), tostring(DvcFQDN)),\n    NativeDeviceHostDomain = coalesce(tostring(DvcDomain), extract(@\"^[^.]+\\.(.+)$\", 1, tostring(DvcFQDN)), extract(@\"^[^.]+\\.(.+)$\", 1, tostring(DvcHostname))),\n    DeviceIp = tostring(DvcIpAddr),\n    DestinationDomain = OcsfDestinationDomain,\n    EntityUrl = coalesce(tostring(Url), tostring(OCSF.finding_info.src_url)),\n    FileValue = coalesce(tostring(FilePath), OcsfFilePath),\n    ProcessIdValue = coalesce(tostring(ProcessId), OcsfProcessId),\n    ProcessCommandLineValue = coalesce(tostring(ProcessCommandLine), OcsfProcessCommandLine),\n    MalwareName = coalesce(tostring(ThreatName), tostring(Malware.name)),\n    FileHashValue = coalesce(tostring(FileSHA256), OcsfSHA256, tostring(FileSHA1), OcsfSHA1, tostring(FileMD5), OcsfMD5)\n| extend\n    SourceIp = OcsfSourceIp,\n    DestinationIp = OcsfDestinationIp,\n    HostValue = coalesce(NativeDeviceHost, OcsfSourceHost),\n    HostDomainValue = iff(isnotempty(NativeDeviceHost), NativeDeviceHostDomain, extract(@\"^[^.]+\\.(.+)$\", 1, OcsfSourceHost))\n| extend\n    PrimaryIp = coalesce(SourceIp, DestinationIp, tostring(DvcIpAddr)),\n    PrimaryIpRole = case(isnotempty(SourceIp), \"Source\", isnotempty(DestinationIp), \"Destination\", isnotempty(tostring(DvcIpAddr)), \"Device\", \"\")\n| extend\n    SynqlyAlertSeverity = case(\n        SeverityValue == \"informational\", \"Informational\",\n        SeverityValue == \"low\", \"Low\",\n        SeverityValue == \"medium\", \"Medium\",\n        SeverityValue in (\"high\", \"critical\", \"fatal\"), \"High\",\n        \"Medium\"),\n    HostNameValue = coalesce(extract(@\"^([^.]+)\", 1, HostValue), HostValue),\n    FileHashAlgorithm = case(\n        isnotempty(FileSHA256) or isnotempty(OcsfSHA256), \"SHA256\",\n        isnotempty(FileSHA1) or isnotempty(OcsfSHA1), \"SHA1\",\n        isnotempty(FileMD5) or isnotempty(OcsfMD5), \"MD5\",\n        \"\")\n| project\n    TimeGenerated,\n    SynqlyAlertId,\n    SynqlyAlertName,\n    SynqlyAlertDescription,\n    SynqlyAlertSeverity,\n    SynqlyTactics,\n    AllAttackTechniques,\n    SynqlyConfidenceLevel,\n    SynqlyConfidenceScore,\n    SynqlyRemediation,\n    SynqlyAlertLink,\n    SourceVendor,\n    SourceProduct,\n    ProductVersion,\n    OriginalSeverity,\n    SourceRuleId,\n    AccountName,\n    AccountSid,\n    AccountDomain,\n    HostNameValue,\n    HostDomainValue,\n    SourceIp,\n    DestinationIp,\n    DeviceIp,\n    PrimaryIp,\n    PrimaryIpRole,\n    DestinationDomain,\n    EntityUrl,\n    FileValue,\n    FileHashAlgorithm,\n    FileHashValue,\n    ProcessIdValue,\n    ProcessCommandLineValue,\n    MalwareName\n",
        "queryFrequency": "PT5M",
        "queryPeriod": "PT5M",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [],
        "techniques": [],
        "templateVersion": "1.0.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}
