Azure Container Registry
ACR is a first-class AAD citizen: every form of authentication ultimately exchanges into an ACR-scoped AAD access token, then exchanges that for a registry refresh token. HarborGuard supports the four practical paths.
Auth options
| Method | When to use | HarborGuard fields |
|---|---|---|
| Service principal (client ID + secret) | Cloud scan, or sensor outside Azure | Username = client ID (UUID), Password = client secret |
| Managed identity | Sensor on Azure VM / AKS / Container Apps | None — sensor uses IMDS |
| AAD user / device-code token | Manual / interactive — short-lived | Username 00000000-0000-0000-0000-000000000000, Password = AAD access token for https://<registry>.azurecr.io |
| Admin user (registry-level) | Quick test only; disabled by policy in most prod tenants | Username + Password from the ACR admin tab |
The wizard auto-detects ACR from the *.azurecr.io suffix.
Add the registry
- Registries → Connect Registry.
- Registry URL —
myregistry.azurecr.io(no path). - The wizard shows
Azure ACR Detected. - Cloud: enter the service principal's client ID + secret.
- Save. The tenant ID isn't entered separately — it's discovered from the AAD challenge returned by
/v2/.
Required role assignments
Assign on the registry resource (or a higher scope):
| Role | Purpose |
|---|---|
AcrPull | Read images. Required. |
AcrDelete | Not needed; HarborGuard never deletes |
AcrPush | Not needed |
Reader (on the resource group) | Optional — only if you want HarborGuard's UI to show resource metadata |
For managed identity, the same role assignment goes to the identity's principal ID.
Push-event sync
ACR Premium SKU emits Event Grid events on every push. Standard and Basic SKUs do not.
- In the registry's Events blade, create a subscription with event type
ImagePushed. - Endpoint:
https://<your-harborguard-host>/api/webhooks/acr. - Set the registry's schedule to
on_push.
Geo-replicated registries emit one event per replica push — HarborGuard de-duplicates by digest within a 60-second window.
Common pitfalls
- AcrPull is not Reader. A principal with
Readeron the resource group cannot pull images. The two roles are independent. - Tag immutability. If you've enabled the
tagImmutabilitypolicy, re-pushing the same tag fails — HarborGuard's "rescan latest" only works if the digest changed or the tag was rebuilt to a new digest. - Content trust / signature requirements. A registry policy that requires signed images blocks anonymous catalog reads but does not affect authenticated pulls. If you see 403s on
/v2/_catalogonly, content trust is the culprit. - Geo-replication. Each replica advertises the same registry name. A sensor in
eastuswith network restrictions targetingeastuswill fail if DNS resolves to thewesteuropereplica — pin the replica via Azure DNS or use Private Link. - Admin user disabled by policy.
Microsoft.ContainerRegistry/registries/disableAdminUseris a common Policy assignment. If admin user was your plan, you need a service principal instead.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Test returns 401 with valid client ID + secret | The service principal's client secret has expired (1- or 2-year default) |
| Test returns 403 after successful auth | Missing AcrPull role assignment |
| Catalog enumerates but specific repos 404 | Repository-scoped tokens issued via tokens resource — HarborGuard expects an account-scoped principal |
| Push events arrive but scans don't run | Schedule is still daily — flip it to on_push after the Event Grid subscription is verified |
| Managed identity works locally, fails on AKS | aad-pod-identity / Workload Identity isn't bound to the sensor pod's service account |