Cloud

Azure ACR and AKS: contain a vulnerable image digest before redeployment

A production runbook for mapping an ACR vulnerability signal to AKS pods, freezing promotion, returning to a known digest, validating the rollout, then retaining or deleting the suspect image.

08 Aug 2026 azureacrakscontainerssecurityimage-digestvulnerabilitysupply-chainkubernetesrunbookrollbackproduction

A vulnerability assessment flags a digest in Azure Container Registry. The repository has several tags, AKS pods are already running, and the delivery pipeline can still promote the image. Deleting the manifest immediately may look like containment. It does not replace running containers, and it can make their next restart fail before a safe digest is ready.

Consider a service deployed to several AKS namespaces from an ACR image. The team must find every place where the suspect digest runs, stop further propagation, move to a known artifact and prove that the cluster cannot recreate the affected version. The runbook ends with an explicit decision: retain the manifest temporarily for investigation, delete it from the registry, or isolate the workload because no safe release is available.

Anchor the incident on the digest

A tag expresses release intent; a digest identifies the manifest that actually runs. One digest can have several tags, and a mutable tag may already point somewhere else. Start the incident record with the full digest, repository, registry and the evidence that raised the alert.

yaml container-image-incident.yml
incident: inc-container-2026-08-08-01
registry: acrprodweu
repository: payments/api
suspect_digest: sha256:<suspect-digest>
finding:
source: approved-vulnerability-assessment
severity: <severity>
affected_component: <package-or-layer>
exploitable_context: under-review
scope:
clusters: [aks-prod-weu, aks-prod-neu]
environments: [production]
containment_owner: platform-oncall
application_owner: payments-team
known_good_digest: sha256:<known-good-digest>
deletion_authorized: false

Do not turn a high severity label into an automatic registry deletion. Confirm that the finding belongs to this digest, that the vulnerable component is present in the execution path and that the image is not merely a parent used during a build stage. That qualification changes urgency; it should not delay the promotion freeze.

Map the ACR manifest to AKS workloads

First, inspect the manifest metadata and every tag currently attached to it. This operation is read-only.

bash 01-inspect-acr-digest.sh
ACR="acrprodweu"
REPOSITORY="payments/api"
SUSPECT_DIGEST="sha256:<suspect-digest>"

az acr manifest list-metadata --registry "$ACR" --name "$REPOSITORY" --query "[?digest=='$SUSPECT_DIGEST']" --output json

Then inspect pods for both the requested image and the image identifier resolved by the runtime. The image field may still display a tag while imageID exposes the digest that was pulled.

bash 02-find-running-digest.sh
kubectl get pods --all-namespaces -o json | jq -r '
.items[] as $pod
| ($pod.status.containerStatuses // [])[]
| select(.imageID | contains("sha256:<suspect-digest>"))
| [
    $pod.metadata.namespace,
    $pod.metadata.name,
    .name,
    .image,
    .imageID,
    (.ready | tostring),
    (.restartCount | tostring)
  ]
| @tsv'

Repeat the inventory for every cluster in scope. Map each pod to its controller and then to the GitOps repository or release that owns desired state. Editing a standalone pod will not hold: a Deployment, StatefulSet, DaemonSet or operator can recreate it from its own image reference.

Include zero-replica workloads and CronJobs. They produce no pod during the initial investigation but can reintroduce the digest on the next scale-out or scheduled execution.

Freeze propagation without destroying evidence

The first change should stop another promotion, not erase the artifact under investigation. Pause the job that moves release tags, deny the digest in the deployment policy and prevent nonessential automated restarts. Keep ACR reads and log collection available.

yaml digest-containment-decision.yml
blocked:
- promote suspect digest to an environment tag
- create a new workload revision with suspect digest
- restart or scale out affected workloads without approval
- rebuild from an unpinned base image

still_allowed:
- read registry metadata and vulnerability evidence
- export manifests, SBOM and deployment history
- build and scan a remediation candidate
- deploy the approved known-good digest to a canary

exit_conditions:
- every desired-state reference is known
- a deployable safe digest is selected
- rollback and isolation paths are assigned
- validation commands are ready before rollout

Moving a tag does not clean existing pods. Deleting from ACR does not stop them either. Containment is complete only when the delivery path rejects the digest and controller desired state can no longer recreate it.

Select a safe return point, not merely an older image

Identify the return candidate by digest and reassess it using the evidence available during the incident. “Deploy the previous version” is not enough if that version shares the same vulnerable layer or cannot run after an irreversible schema migration.

At minimum, verify build provenance, current scan results, configuration compatibility, external dependencies, irreversible migrations and startup against the present state. When no previous digest is defensible, build a patched image from a pinned base or isolate the service. Do not restore the suspect digest merely because the remediation rollout fails.

text remediation-choice.txt
Return to a known digest
digest rescanned and provenance verified
compatible with the current schema and configuration
startup probe available
another safe release is ready if it regresses

Build a patched digest
no previous release is compatible
dependency or base-image patch is required
reproducible pipeline and attestations are available

Isolate the workload
active exploitation or high impact is confirmed
no safe digest can be deployed immediately
network exposure or business action can be suspended
restoration requires a new validation decision

Redeploy by digest and start with a canary

Change the source of truth, not just the live cluster. A digest reference ensures that every new pod requests the same manifest even when someone later moves a tag.

yaml deployment-known-good.patch.yml
spec:
template:
  metadata:
    annotations:
      naxaya.com/image-incident: inc-container-2026-08-08-01
  spec:
    containers:
    - name: api
      image: acrprodweu.azurecr.io/payments/api@sha256:<known-good-digest>
      imagePullPolicy: IfNotPresent

Apply the change to one replica or a bounded traffic segment first. Validate startup, readiness, application errors, dependencies and a reversible business transaction. Expand in explicit stages. A Deployment becoming Available is not enough; the chosen digest must process representative traffic correctly.

bash 03-validate-rollout.sh
NAMESPACE="payments"
DEPLOYMENT="payments-api"

kubectl rollout status --namespace "$NAMESPACE" deployment/"$DEPLOYMENT" --timeout=10m

kubectl get pods --namespace "$NAMESPACE" -l app=payments-api -o custom-columns='POD:.metadata.name,IMAGE:.spec.containers[*].image,IMAGE_ID:.status.containerStatuses[*].imageID,READY:.status.containerStatuses[*].ready'

After rollout, run the global suspect-digest inventory again. It should return no current pod, newly scheduled pod or job started during the change window. Also verify that GitOps manifests, CronJobs and suspended workloads no longer reference it.

Decide the fate of the suspect manifest

Deleting by digest from ACR removes the manifest and every tag that references it. The operation is destructive. It prevents future pulls from that registry, but it neither stops existing containers nor protects a restart that still points at the deleted digest.

Require three proofs before deletion: no approved desired state references the digest, no recovery dependency needs it, and the evidence required for the investigation has been retained under the organization’s policy. Display the exact target first, then obtain authorization for the destructive step.

bash 04-delete-suspect-manifest-after-approval.sh
ACR="acrprodweu"
REPOSITORY="payments/api"
SUSPECT_DIGEST="sha256:<suspect-digest>"

az acr manifest list-metadata --registry "$ACR" --name "$REPOSITORY" --query "[?digest=='$SUSPECT_DIGEST'].[digest,tags,lastUpdateTime]" --output table

# Run only after formal validation of the target.
az acr repository delete --name "$ACR" --image "$REPOSITORY@$SUSPECT_DIGEST" --yes

When the investigation requires artifact retention, do not let retention become a promotion path. Keep the pipeline and admission block in place, restrict write permissions and record a review date.

Validate containment and prepare rollback

Rolling back the rollout must never mean “restore the suspect digest.” Prepare two paths: switch to a second safe digest if the selected release regresses, or isolate the workload when no clean image works with the current state.

Close the incident only when the suspect digest is no longer running, no source of truth references it, a promotion attempt is denied, the replacement digest is observed in every cluster, probes remain healthy, and the retention or deletion decision is recorded.

After deletion, test a controlled pull of the suspect digest from an environment with no local cache: it must fail. Then reschedule the healthy canary on a node that does not already hold its layers. This second test proves that the replacement digest remains retrievable from ACR.

Conclusion

A container finding becomes actionable when it is tied to a digest, the pods that run it and the controllers that can recreate it. The safe order is to freeze promotion, inventory exposure, select a clean digest, redeploy by digest, validate behavior and only then decide what happens to the suspect manifest.

The final decision must prove three separate properties: the vulnerable digest no longer runs, it cannot be promoted again, and the service has a rollback path that does not reintroduce the same risk.