Skip to main content

Enable Image Vulnerability Scanning With Microsoft Defender

More Info:

Enable Microsoft Defender for Cloud image scanning (or a third-party provider) on your Azure Container Registry to detect vulnerabilities in stored container images before they are deployed.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. Identify target subscription(s) and registry(ies)

    • On any machine with Azure CLI access:
      az account show --output table
      az acr list --output table
    • Decide which ACRs are in scope for the cluster (e.g., those referenced in your deployment manifests, Helm charts, or imagePullSecrets).
  2. Check whether Microsoft Defender for Cloud is enabled for Container Registries

    • On any machine with Azure CLI access:
      az security pricing show --name ContainerRegistry --output table
    • If pricingTier is Standard, MDC is enabled for Container Registries at the subscription level; if Free, MDC image scanning is not active.
  3. Check whether image scanning is enabled on each target ACR

    • For each in-scope registry (replace placeholders with real values):
      az resource show \
      --ids /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.ContainerRegistry/registries/<REGISTRY_NAME> \
      --output json | jq '.properties.enabled'
    • Review the true/false value; true indicates image scanning is enabled on that registry.
  4. Decide on the protection model (MDC vs. third-party)

    • If you already use a third-party scanner (e.g., integrated via CI/CD or ACR tasks), gather evidence (tool config, sample scan reports, and how they cover all ACR images/tags).
    • Compare its coverage and SLAs to MDC’s capabilities; decide if MDC should be enabled, used in combination, or if the third-party solution alone is sufficient for your risk appetite.
  5. Enable or adjust configuration where required

    • If you decide to enable MDC for Container Registries at subscription level (incurs cost):
      az security pricing create --name ContainerRegistry --tier Standard --output table
    • If you need to enable image scanning on a specific registry:
      az resource update \
      --ids /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.ContainerRegistry/registries/<REGISTRY_NAME> \
      --set properties.enabled=true \
      --output json
    • If you decide not to enable MDC (because of cost or reliance on a third-party solution), document the justification and attach evidence of the alternative scanning.
  6. Verify and document the final state

    • Re-run:
      az security pricing show --name ContainerRegistry --output table
    • And for each in-scope registry:
      az resource show \
      --ids /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.ContainerRegistry/registries/<REGISTRY_NAME> \
      --output json | jq '.properties.enabled'
    • Record outputs, decisions (MDC vs. third-party), and any exclusions as part of your security and compliance documentation.
Using kubectl

kubectl cannot enable Microsoft Defender for Cloud image scanning or configure Azure Container Registry; this must be done in the Azure portal, Azure CLI, or your IaC targeting the subscription/registry configuration. Refer to the Manual Steps section for the exact Azure-side commands and review process.

Automation
#!/usr/bin/env bash
#
# Purpose:
# Enumerate container images used in the AKS cluster and report whether
# Microsoft Defender for Cloud (MDC) image scanning is enabled on the
# backing Azure Container Registries (ACR), where detectable from the CLI.
#
# Requirements:
# - Run on any machine with:
# * kubectl configured for the target cluster
# * Azure CLI (`az`) installed and logged in
# - You must have permission to:
# * List all pods in the cluster
# * Read ACR resources and Security pricing in the subscription(s)
#
# NOTE:
# This script ONLY reports state. It does NOT and CANNOT auto-remediate
# this CIS MANUAL control.

set -euo pipefail

echo "=== Step 1: Discover all unique container image registries used in the cluster ===" >&2

# Collect all images from all namespaces and deduplicate
IMAGES=$(
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{range .spec.initContainers[*]}{.image}{"\n"}{end}{end}' 2>/dev/null \
| sed '/^$/d' \
| sort -u
)

if [ -z "${IMAGES}" ]; then
echo "No images found in cluster (no pods or no containers)." >&2
exit 0
fi

echo "Discovered images:"
echo "${IMAGES}" | sed 's/^/ - /'

# Extract registry hostname from each image (e.g. myregistry.azurecr.io from myregistry.azurecr.io/ns/app:tag)
REGISTRIES=$(
echo "${IMAGES}" \
| awk -F/ '
# image formats:
# registry/namespace/name:tag
# registry/name:tag
# name:tag (Docker Hub implicit)
{
if (NF == 1) {
# No explicit registry (Docker Hub or other implicit) – report as "<implicit>"
print "<implicit>"
} else {
print $1
}
}
' \
| sort -u
)

echo
echo "=== Step 2: Identify Azure Container Registries (ACR) among used registries ===" >&2
echo "All registries in use:"
echo "${REGISTRIES}" | sed 's/^/ - /'

# Filter registries that look like ACR (ends with .azurecr.io)
ACR_REGISTRIES=$(
echo "${REGISTRIES}" | grep '\.azurecr\.io$' || true
)

if [ -z "${ACR_REGISTRIES}" ]; then
echo
echo "No Azure Container Registries (.azurecr.io) detected in current workloads."
echo "If you expect ACR usage or use third-party image scanning, review that configuration manually."
exit 0
fi

echo
echo "Azure Container Registries detected in workloads:"
echo "${ACR_REGISTRIES}" | sed 's/^/ - /'

echo
echo "=== Step 3: Map ACR registries to Azure resources and check MDC pricing ===" >&2

# List all ACR resources accessible via Azure CLI
ACR_LIST_JSON=$(az acr list --query '[].{name:name, loginServer:loginServer, id:id}' -o json)

# Check MDC pricing for ContainerRegistry at subscription scope
# Note: This returns the plan at the *current* az account subscription.
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
MDC_PRICING_JSON=$(az security pricing show --name ContainerRegistry -o json || echo '{}')

MDC_TIER=$(echo "${MDC_PRICING_JSON}" | jq -r '.pricingTier // empty')

echo
echo "Current subscription: ${SUBSCRIPTION_ID}"
if [ -z "${MDC_TIER}" ] || [ "${MDC_TIER}" = "null" ]; then
echo "MDC pricing for ContainerRegistry could not be determined (no plan or insufficient permissions)."
else
echo "MDC ContainerRegistry pricing tier for this subscription: ${MDC_TIER}"
fi

echo
echo "=== Step 4: Per-registry report ==="
printf "%-30s %-40s %-12s %-8s\n" "REGISTRY" "ACR_RESOURCE_ID" "MDC_TIER" "STATUS"
printf "%-30s %-40s %-12s %-8s\n" "--------" "--------------" "--------" "------"

# For each used ACR registry, try to find its resource and report
while read -r REG; do
[ -z "${REG}" ] && continue
# Extract ACR name from login server: <name>.azurecr.io
ACR_NAME="${REG%%.azurecr.io}"

# Find matching ACR resource
ACR_ID=$(echo "${ACR_LIST_JSON}" | jq -r --arg login "${REG}" '.[] | select(.loginServer==$login) | .id' | head -n1)

if [ -z "${ACR_ID}" ] || [ "${ACR_ID}" = "null" ]; then
printf "%-30s %-40s %-12s %-8s\n" "${REG}" "NOT_FOUND" "-" "REVIEW"
continue
fi

# Check if MDC image scanning is enabled on this registry, if property exists
# NOTE: This property name comes from the benchmark example; real environments may differ.
ACR_JSON=$(az resource show --ids "${ACR_ID}" -o json || echo '{}')
ENABLED_PROP=$(echo "${ACR_JSON}" | jq -r '.properties.enabled // empty')

STATUS="UNKNOWN"
if [ -n "${ENABLED_PROP}" ] && [ "${ENABLED_PROP}" != "null" ]; then
if [ "${ENABLED_PROP}" = "true" ]; then
STATUS="OK"
else
STATUS="PROBLEM"
fi
else
# Fall back to subscription-level MDC tier if available
if [ "${MDC_TIER}" = "Standard" ]; then
STATUS="POSSIBLY_OK"
else
STATUS="PROBLEM"
fi
fi

printf "%-30s %-40s %-12s %-8s\n" "${REG}" "${ACR_ID}" "${MDC_TIER:-"-"}" "${STATUS}"
done <<< "${ACR_REGISTRIES}"

cat <<'EOF'

How to interpret STATUS:

- OK
- Registry-level image scanning appears enabled (properties.enabled == true).
- POSSIBLY_OK
- Registry does not expose a clear 'enabled' flag, but subscription-level MDC
pricing for ContainerRegistry is set to Standard. Review in Azure Portal
(Defender for Cloud -> Environment settings -> [Subscription] -> Defender plans).
- PROBLEM
- Registry is found, but:
* properties.enabled is false, or
* MDC pricing tier is not Standard and no other scanning is detected.
- This indicates the CIS control is likely NOT met for this registry.
- REVIEW
- Registry used in workloads but no matching ACR resource was found in the
current Azure context. This may mean:
* It is in another subscription/tenant, or
* It is a non-ACR registry (e.g., Docker Hub, other provider), or
* You lack permissions.
- Manually verify that a vulnerability scanning solution (MDC or third-party)
is enabled for that registry.

Note:
- This script does NOT configure MDC or third-party scanning.
- Use it regularly to identify registries and subscriptions that require
manual review and potential configuration changes.
EOF