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
Remediation
Manual Steps
-
Identify target subscription(s) and registry(ies)
- On any machine with Azure CLI access:
az account show --output tableaz 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).
- On any machine with Azure CLI access:
-
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
pricingTierisStandard, MDC is enabled for Container Registries at the subscription level; ifFree, MDC image scanning is not active.
- On any machine with Azure CLI access:
-
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/falsevalue;trueindicates image scanning is enabled on that registry.
- For each in-scope registry (replace placeholders with real values):
-
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.
-
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.
- If you decide to enable MDC for Container Registries at subscription level (incurs cost):
-
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.
- Re-run:
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