Using Defender for Storage to scan for malware in a Storage Account, Event Grid to publish details and trigger Logic App

As a continuation to my previous post:

Creating a web application with .NET Core for uploading files into an Azure Storage Account
https://blog.terenceluk.com/2024/10/creating-a-web-application-with-net-core-for-uploading-files-into-an-azure-storage-account.html

This post serves to demonstrate the next step for the flow of events as shown in the diagram with the focus highlighted in yellow:

If virus/malware scanning was not a requirement then we can simply use an Event Grid to publish file uploads from storage account events but for this design, I would like to have Defender for Storage scan for malware and use the results from the scan to trigger the next series of events. Defender for Storage has come a long way since it was first release and the features have matured quite a bit. The documentation for malware scanning can be found here: https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-storage-malware-scan

Step #1 – Turn on Defender for Storage

Let’s begin by enabling Defender for Storage for the test account:

Once Defender for Storage is enabled, you will find a new System topic created with the storage name followed by a unique [I assume] GUID.

The System topic will have an Event Subscription created that subscribes to a webhook for the Defender for Storage malware scanning events:

Step #2 – Configure Event Grid Topic to subscribe to Defender for Storage

With Defender for Storage turned on, we can now create an Event Grid topic for scan results to send to:

Step #3 – Configure Logic App to receive Event Grid Topic subscription trigger

Before we proceed to create a subscription in the Event Grid Topic we created, we’ll need to create the Logic App so we can retrieve the webhook URL. Create a new Logic App, add the trigger When a HTTP request is received, then save the Logic App to get a HTTP URL (this won’t be displayed until the Logic App is saved):

Now open the trigger and use the Use sample payload to generate schema to generate a schema by pasting the following sample JSON payload:

[
{
“id”: “ec667a90-0000-4948-accd-6b237adceb3a”,
“subject”: “storageAccounts/contosotest001/containers/uploads/blobs/Test-Defender03.txt”,
“data”: {
“correlationId”: “ec667a90-0359-4948-accd-6b237adceb3a”,
“blobUri”: “https://contosotest001.blob.core.windows.net/uploads/Test-Defender03.txt”,
“eTag”: “0x8DCE730957D80DE”,
“scanFinishedTimeUtc”: “2024-10-08T00:31:46.0127867Z”,
“scanResultType”: “Malicious”,
“scanResultDetails”: {
“malwareNamesFound”: [
“Virus:DOS/EICAR_Test_File”
],
“sha256”: “275A021BBFB6489E54D471899F7DB9D1663FC695EC2FE2A2C4538AABF651FD0F”
}
},
“eventType”: “Microsoft.Security.MalwareScanningResult”,
“dataVersion”: “1.0”,
“metadataVersion”: “1”,
“eventTime”: “2024-10-08T00:31:46.0143056Z”,
“topic”: “/subscriptions/5be8ba9b-0000-49d3-000-c0a99632cdc3/resourceGroups/rg-dev-test/providers/Microsoft.EventGrid/topics/test-eventgrid-topic”
}
]

This should generate a schema like the following:

{
    “type”: “array”,
    “items”: {
        “type”: “object”,
        “properties”: {
            “id”: {
                “type”: “string”
            },
            “subject”: {
                “type”: “string”
            },
            “data”: {
                “type”: “object”,
                “properties”: {
                    “correlationId”: {
                        “type”: “string”
                    },
                    “blobUri”: {
                        “type”: “string”
                    },
                    “eTag”: {
                        “type”: “string”
                    },
                    “scanFinishedTimeUtc”: {
                        “type”: “string”
                    },
                    “scanResultType”: {
                        “type”: “string”
                    },
                    “scanResultDetails”: {
                        “type”: “object”,
                        “properties”: {
                            “malwareNamesFound”: {
                                “type”: “array”,
                                “items”: {
                                    “type”: “string”
                                }
                            },
                            “sha256”: {
                                “type”: “string”
                            }
                        }
                    }
                }
            },
            “eventType”: {
                “type”: “string”
            },
            “dataVersion”: {
                “type”: “string”
            },
            “metadataVersion”: {
                “type”: “string”
            },
            “eventTime”: {
                “type”: “string”
            },
            “topic”: {
                “type”: “string”
            }
        },
        “required”: [
            “id”,
            “subject”,
            “data”,
            “eventType”,
            “dataVersion”,
            “metadataVersion”,
            “eventTime”,
            “topic”
        ]
    }
}
Proceed to copy the HTTP URL and save the trigger:
Navigate back to the Event Grid Topic created earlier and create a new Event Subscription:
Provide a name and configure the Web Hook endpoint for the Logic App, then create:
You should now see a subscription created where it can be used to capture all Defender for Storage account activity then sent to the Logic App:
The last step is to configure Defender for Cloud to use this Event Grid Topic:
You can also send the events to Log Analytics which is something I prefer as it allows for auditing and troubleshooting:
To test the events, simply upload a file that does not contain a virus and one that does. A sample virus file that I use is the EICAR test file found here:  https://www.eicar.org/download-anti-malware-testfile/
Simply create a text file and paste the EICAR string in.
Proceed to upload the files and you should see the Logic App triggered and ran:
The following is an example of a clean file with no virus or malware where the details can be found in the body:
[
{
“id”: “0b582354-8626-459b-92c0-58bc84aacf9d”,
“subject”: “storageAccounts/contosotest001/containers/uploads/blobs/Test-Defender02.txt”,
“data”: {
“correlationId”: “0b582354-8626-459b-92c0-58bc84aacf9d”,
“blobUri”: “https://contosotest001.blob.core.windows.net/uploads/Test-Defender02.txt”,
“eTag”: “0x8DCE73009331803”,
“scanFinishedTimeUtc”: “2024-10-08T00:27:48.6998418Z”,
“scanResultType”: “No threats found”,
“scanResultDetails”: null
},
“eventType”: “Microsoft.Security.MalwareScanningResult”,
“dataVersion”: “1.0”,
“metadataVersion”: “1”,
“eventTime”: “2024-10-08T00:27:48.7003571Z”,
“topic”: “/subscriptions/5be8ba9b-000-0000-806f-c0a99632cdc3/resourceGroups/rg-dev-test/providers/Microsoft.EventGrid/topics/test-eventgrid-topic”
}
]
The following is where malware was detected, which is the payload we used to define the schema:
[
{
“id”: “ec667a90-0000-4948-accd-6b237adceb3a”,
“subject”: “storageAccounts/contosotest001/containers/uploads/blobs/Test-Defender03.txt”,
“data”: {
“correlationId”: “ec667a90-0359-4948-accd-6b237adceb3a”,
“blobUri”: “https://contosotest001.blob.core.windows.net/uploads/Test-Defender03.txt”,
“eTag”: “0x8DCE730957D80DE”,
“scanFinishedTimeUtc”: “2024-10-08T00:31:46.0127867Z”,
“scanResultType”: “Malicious”,
“scanResultDetails”: {
“malwareNamesFound”: [
“Virus:DOS/EICAR_Test_File”
],
“sha256”: “275A021BBFB6489E54D471899F7DB9D1663FC695EC2FE2A2C4538AABF651FD0F”
}
},
“eventType”: “Microsoft.Security.MalwareScanningResult”,
“dataVersion”: “1.0”,
“metadataVersion”: “1”,
“eventTime”: “2024-10-08T00:31:46.0143056Z”,
“topic”: “/subscriptions/5be8ba9b-0000-49d3-000-c0a99632cdc3/resourceGroups/rg-dev-test/providers/Microsoft.EventGrid/topics/test-eventgrid-topic”
}
]
Since we configured Log Analytics for Defender for Storage, we will also see the events captured when we query the logs:
I hope this provides a good walkthrough of how to set up Defender for Storage events that can be consumed by a Logic App to process files that are uploaded and scanned for viruses and malware in a storage account. My next post will continue with the automation.