Loading...

How to secure access to an Azure Container registry with RBAC

How to secure access to an Azure Container registry with RBAC

This post is part of a series

I like to deploy my Azure resources using Bicep - If you never heard about it, I blogged a while ago on how to get started with Bicep - please catch up first!


How to Secure Access to an Azure Container Registry with RBAC

We discussed in the previous post how an Azure Container Registry may help you standardize and ease the work with deployment files. One of the questions I received (thanks Christian 👏) and wanted to address in this post was why not just use git (for example in Azure repos)? The answer is, that while git is amazing for during development, enabling teamwork, version control, traceability, images published to Azure Container Registry are immutable, so that they can’t be modified anymore. This ensures, that the files we work with are not being altered anymore. In this post, we’ll explore how to now secure access to an Azure Container Registry (ACR) using Role-Based Access Control (RBAC).

Before we start

Hate to break it to you, but there is a standard setting in Azure Container Registry, that I very much dislike.

It’s called Azure Container Registry Admin Access and allows accessing the entire ACR as an admin. As if that wasn’t scary enough, for everyone who like to read audit logs as good night stories: If this stays enabled (because yes, this is ON by default 😱), it’s always the admin’s username appearing in the log files. That will make traceability of any issue really hard. So do yourself and me a favor and turn this off.

acr admin acess

Ok, I’m glad we can finally proceed :-)

Understanding Azure RBAC for Container Registry

Azure Container Registry (ACR) supports a set of built-in Azure roles that provide different levels of permissions. These roles can be assigned to users, service principals, or other identities to control access to the registry. The primary roles include:

  • Owner: Full access to all resources
  • Contributor: Can manage all resources but cannot grant access to others
  • Reader: Can view all resources but cannot make changes
  • AcrPush: Can push container images to the registry
  • AcrPull: Can pull container images from the registry

Using Azure RBAC, you can assign these roles to ensure that only authorized identities can interact with your registry, whether it’s for pulling or pushing container images. For example, someone would be the person to exclusively push images (for examples the bicep deployment files from the last blog post) to ACR, but a set of developers should be able to consume (pull) the imaged from ACR.

Creating a Service Principal

To automate interactions with your ACR, you can create a service principal and assign it the necessary RBAC roles. I’d like to use a Key Vault then to securely store the credentials. Let’s go:


# Variables
$vaultName = "<name of your key vault>"
$spName = "<name of the service principal>"
$subscriptionId = "<your Azure subscription id>"
$resourceGroup = "name of the resource group>"
$acrName = "<name of your Azure Container Registry>"
$location = "<name of the location>"
# Create a service principal and capture the output
$spOutput = az ad sp create-for-rbac --name $spName --role acrpull --scopes "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.ContainerRegistry/registries/$acrName" --query "{appId: appId, password: password, tenant: tenant}" -o json
# Parse the JSON output to extract appId, password, and tenant
$spDetails = $spOutput | ConvertFrom-Json
$appId = $spDetails.appId
$password = $spDetails.password
$tenantId = $spDetails.tenant

Store the values in Azure Key Vault

Now we want to store the values in an Azure Key Vault. If you don’t already have one (and permissions to write to it), let’s create one first:


az keyvault create --name $vaultName --resource-group $resourceGroup --location $location

To be able to push values into the Key Vault, we need to assign an RBAC role Key Vault Secrets Officer. So let’s get that done as well:

# Assign the Key Vault Secrets Officer role to your user
az role assignment create --assignee $userObjectId --role "Key Vault Secrets Officer" --scope "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.KeyVault/vaults/$vaultName"

💡RBAC in Key Vault can be a bit tricky, as sometimes we will need to allow a bit more of propagation time for the role assignment to be fully effective. We can solidify our little script like this:


# Wait for role assignment to propagate
$maxRetries = 10
$retryCount = 0
$roleAssigned = $false
while (-not $roleAssigned -and $retryCount -lt $maxRetries) {
Start-Sleep -Seconds 10
$roleAssignment = az role assignment list --assignee $userObjectId --scope "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.KeyVault/vaults/$vaultName" --query "[?roleDefinitionName=='Key Vault Secrets Officer']" -o json | ConvertFrom-Json
if ($roleAssignment -ne $null) {
$roleAssigned = $true
} else {
$retryCount++
Write-Output "Waiting for role assignment to propagate... ($retryCount/$maxRetries)"
}
}
if (-not $roleAssigned) {
Write-Error "Role assignment failed or has not propagated yet. Please try again after some time."
exit 1
}

Once that is completed successfully, we will push our credentials into the Key Vault


az keyvault secret set --vault-name $vaultName --name acr-sp-id --value $appId
az keyvault secret set --vault-name $vaultName --name acr-sp-password --value $password
az keyvault secret set --vault-name $vaultName --name acr-tenant-id --value $tenantId
Write-Output "Service principal credentials stored in Key Vault successfully ✅."

Lets briefly check this in the Azure portal:

  • Select the resource group in which the ACR resides
  • Select the ACR
  • Select Access Control (IAM)
  • Select Check access
  • Search for the name of your service principal
  • Select it - You can now see an overview of the roles of the service principal. In our case, it’s the acr-pull role. You can select this now again and view the permissions associated to this role.

acr-role assignment

Conclusion

We created a service principal, stored its credentials securely in an Azure Key Vault and assigned an RBAC role to it which will only grant pull permissions to the Azure Container Registry. This ensures, that using this service principal, no new images can’t be published to the ACR. The Azure Container Registry stores our published Bicep 💪 files and makes them ready to deploy, reducing the time it takes to write the Infrastructure as Code significantly.

Stay tuned for the next part of this series, where I’ll guide you through using the same concept but instead of a service principal with a Managed Identity (and why I prefer this even more).

Questions? Let me know!

Published on:

Learn more
Luise Freese: Consultant & MVP
Luise Freese: Consultant & MVP

Recent content on Luise Freese: Consultant & MVP

Share post:

Related posts

You are holding GitHub Copilot Wrong!

Most developers think that one can’t really use GitHub Copilot wrong. There is a chat interface that lets you also choose a model, so th...

8 months ago

You are holding GitHub Copilot Wrong!

Part 0 showed why constant prompting, re-prompting, and steering GitHub Copilot feels fragile. Not because Copilot is unreliable, but because ...

8 months ago

Building a Multi-Hierarchy Ticket Classification System (Because Keywords Aren't Enough)

Look, I love a good keyword-based system as much as the next developer. They’re fast, predictable, and when your user says “VPN,&r...

8 months ago

Secretless cross-tenant dataverse access

Client secrets are like hiding your house key under the mat; easy to grab and impossible to audit. Certificates are just slightly better, beca...

10 months ago

How Azure CLI handles your tokens and what you might be ignoring

Running az login feels like magic. A browser pops up, you pick an account, and from then on, everything just works. No more passwords, no more...

10 months ago

How Dev Proxy teaches you to make your apps more resilient

I added Microsoft Dev Proxy to my Mermaid → Dataverse converter, because I wanted to test how it handled rate limits and API errors. What I go...

10 months ago

Building Azure functions that never store secrets — ever

What if your function could hit Microsoft Graph with no client secrets, no certs, and no Key Vault entries? That is exactly what a Managed id...

10 months ago

Introducing Mermaid to Dataverse Converter

Why diagrams matter (and why they usually fail us) Entity-Relationship Diagrams (ERDs) are the universal shorthand for talking about data mode...

11 months ago

It’s OK to be seen trying

It’s OK to be seen trying Somewhere along the way, we started believing that you’re only allowed to speak after you’ve figured everything out....

1 year ago

Stuck in pilot - Part 1: no foundations, no future

“We just need to test the AI. We’ll figure out the data later.” That sentence has quietly killed more AI pilots than any model failure ever ...

1 year ago

Newsletter

Get the latest Dynamics 365 and Power Platform content in your inbox

A curated digest of community blogs, product news, videos, and podcasts — delivered without the noise.

Weekly updates Unsubscribe anytime Fresh community picks
We use your email only for the newsletter and you can unsubscribe at any time.
By subscribing, you agree to the privacy policy.