Previously I wrote an article to walk people through CI/CD Integration with Azure Security Center. I got a question about uploading vulnerability assessment result to an Azure Storage Account.
In this article, let’s see how to do that with Azure CLI GitHub Action.
Assume you have completed the workflow to scan your docker image using the Azure Container Scan GitHub action. The status has been reflected in Azure Security Center as well. Now you are asked to upload the vulnerability assessment result into an Azure Storage Account so you can download to read it offline or you want to integrate with Azure Logic App or Azure Function for notification or further action.
In this scenario, all you need to do is just log into Azure and run az storage blob upload
There are three steps to complete this action:
- Step 1 – Create a Service Principal to authenticate with Azure
- Step 2 – Create secrets to store storage account access key and credential
- Step 3 – Update workflow to upload assessment result
- Step 4 – Notify container image vulnerability assessment result to email using Azure Logic App
Step 1 – Create a Service Principal to authenticate with Azure
Use the following Azure CLI to create a new service principal and grant it Contributor role in your resource group scope:
RG_NAME='azsec-acr-rg' SUB_ID=$(az account show --query id -o tsv) SP_NAME='sp-for-storage' az ad sp create-for-rbac -n "${SP_NAME}" \ --role Contributor \ --scopes /subscriptions/$SUB_ID/resourceGroups/$RG_NAME \ --sdk-auth
Copy the output and re-format it as follows:
{"clientId": "3ce96a3f-84e5-XXXX-ae93-2482a72f984b", "clientSecret": "XXXX~jkcuW-XXXXX.BehlMCJ", "subscriptionId": "67d6179d-XXXX-4ccd-XXXX-4d3ff2e13349", "tenantId": "03987603-0fc0-XXX-XXX-cdffbefb2226", "activeDirectoryEndpointUrl": "https://login.microsoftonline.com", "resourceManagerEndpointUrl": "https://management.azure.com/", "activeDirectoryGraphResourceId": "https://graph.windows.net/", "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/", "galleryEndpointUrl": "https://gallery.azure.com/", "managementEndpointUrl": "https://management.core.windows.net/"}
The following format won’t be accepted:
{ "clientId": "3ce96a3f-84e5-XXXX-ae93-2482a72f984b", "clientSecret": "XXXX~jkcuW-XXXXX.BehlMCJ", "subscriptionId": "67d6179d-XXXX-4ccd-XXXX-4d3ff2e13349", "tenantId": "03987603-0fc0-XXX-XXX-cdffbefb2226", "activeDirectoryEndpointUrl": "https://login.microsoftonline.com", "resourceManagerEndpointUrl": "https://management.azure.com/", "activeDirectoryGraphResourceId": "https://graph.windows.net/", "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/", "galleryEndpointUrl": "https://gallery.azure.com/", "managementEndpointUrl": "https://management.core.windows.net/" }
After service principal creation you also need a storage account and container to store the assessment result.
Step 2 – Create secrets to store storage account access key and credential
Use Github Secret to store service principal credentials and storage account key. In my demonstration I created the following secrets in GitHub:
- AZ_CREDENTIAL
- AZ_STORAGE_KEY
Step 3 – Update workflow to upload assessment result
After you have a service principal and a storage account with container ready now let’s update the workflow:
- name: Login to Azure uses: azure/login@v1 with: creds: ${{ secrets.AZ_CREDENTIAL }} allow-no-subscriptions: true - name: Upload to storage account run: | az account set -s ${{ env.AZ_SUBCRIPTION_ID }} az storage blob upload --account-name ${{ env.AZ_STORAGE_ACCOUNT_NAME }} \ --account-key ${{ secrets.AZ_STORAGE_KEY }} \ --container-name ${{ env.AZ_STORAGE_CONTAINER_NAME }} \ --file ${{ steps.container-scan.outputs.scan-report-path }} \ --name ${{ github.sha }}.json
Ensure to add environment variable for your storage account name and container name.
Go to storage account to download and verify the result. Below is the sample result of the testing docker image:
{ "imageName": "ascacr0731.azurecr.io/azsec-firefox:7f83011c0596de71dc61c4f47b9e7033213b60da", "vulnerabilities": [ { "vulnerabilityId": "CVE-2021-33910", "packageName": "libsystemd0", "severity": "HIGH", "description": "basic/unit-name.c in systemd prior to 246.15, 247.8, 248.5, and 249.1 has a Memory Allocation with an Excessive Size Value (involving strdupa and alloca for a pathname controlled by a local attacker) that results in an operating system crash.", "target": "ascacr0731.azurecr.io/azsec-firefox:7f83011c0596de71dc61c4f47b9e7033213b60da (ubuntu 18.04)" }, { "vulnerabilityId": "CVE-2021-33910", "packageName": "libudev1", "severity": "HIGH", "description": "basic/unit-name.c in systemd prior to 246.15, 247.8, 248.5, and 249.1 has a Memory Allocation with an Excessive Size Value (involving strdupa and alloca for a pathname controlled by a local attacker) that results in an operating system crash.", "target": "ascacr0731.azurecr.io/azsec-firefox:7f83011c0596de71dc61c4f47b9e7033213b60da (ubuntu 18.04)" } ], "bestPracticeViolations": [ { "code": "DKL-DI-0005", "title": "Clear apt-get caches", "level": "FATAL", "alerts": "Use 'rm -rf /var/lib/apt/lists' after 'apt-get install' : |1 firefox_version=88.0.1 /bin/sh -c apt-get update && apt-get install -y wget && rm -rf /var/lob/apt/lists/*" }, { "code": "CIS-DI-0001", "title": "Create a user for the container", "level": "WARN", "alerts": "Last user should not be root" }, { "code": "CIS-DI-0005", "title": "Enable Content trust for Docker", "level": "INFO", "alerts": "export DOCKER_CONTENT_TRUST=1 before docker pull/build" }, { "code": "CIS-DI-0006", "title": "Add HEALTHCHECK instruction to the container image", "level": "INFO", "alerts": "not found HEALTHCHECK statement" }, { "code": "CIS-DI-0008", "title": "Confirm safety of setuid/setgid files", "level": "INFO", "alerts": "setuid file: urwxr-xr-x usr/bin/chfn" } ], "vulnerabilityScanTimestamp": "2021-07-31T22:55:03.609Z" }
Now you have several options for further steps:
- Notify your DevOps team and cc to InfoSec manager about the result.
- Copy/import vulnerable image to another container registry for further review using az acr import CLI
- Read the result and decide what to do next

Sample email notification after an image is scanned.
Pingback: Quick look at CICD Integration in Azure Security Center to scan your docker image -Microsoft Azure Security Randomness
Pingback: Notify container image vulnerability assessment result to email using Azure Logic App -Microsoft Azure Security Randomness