How many Entra App Registrations do I need?
25 Oct 2023
Creating an App Registration (and its corresponding Service Principal) in Microsoft Entra ID (formerly known as “Azure Active Directory,” “Azure AD,” or “AAD”) lets a code-deployment-automation “CI/CD pipeline” (such as an Azure DevOps Pipeline or a GitHub Action) access the Azure cloud resources onto which you’d like to deploy your code.
But just how many of them do you need per project / application / workload?
It depends a lot upon how your team likes to organize and name things. (Remember, it’s generally considered better for a software development team to stick to a single standard than for that standard to be “perfect.”)
Creating more than 1 app registration might help you leverage the concept of privilege separation to reduce accidents and incidents by following the principal of least privilege.
That said, it’s not the only way to separate privileges. Also influential are:
- The separation (or lack thereof) of repositories and deployment automations in your Git repository host (such as Azure DevOps Pipelines, GitHub, etc.).
- Various security-oriented settings in your Git repository host.
- The Tenant / Subscription / Resource Group patterns into which your company clumps Azure resources.
- Choosing the least-privileged Azure RBAC role that can accomplish a necessary task.
- (e.g. “Website Contributor” instead of “Contributor.”)
Example architecture
- Your team of software developers works within the Payroll subdepartment of the Human Resources (“HR”) department at your company.
- You’re rolling out a big project known as the “Hello World” project.
- Your infrastructure isn’t the type where you can have 100% of your nonproduction runtime environments be ephemeral, so you first deploy most of your code into a lowest “
dev
” nonproduction environment, then deploy it again into a higher “stg
” environment, and finally deploy it into a production “prd
” environment. - Each environment contains the following Azure resources:
- An Azure Key Vault
- An Azure Storage Blob
- An Azure Data Factory with a System-Assigned Managed Identity (“SAMI”) enabled.
- Its
dev
version automatically backs itself up into a Git-tracked code repository calledhr-payroll-hw-adf
.
- Its
- An Azure Function App nicknamed “bonjour” with a SAMI enabled.
- Git repo name
hr-payroll-hw-func-bonjour
.
- Git repo name
- An Azure Function App nicknamed “hola.”
- Git repo name
hr-payroll-hw-func-hola
- Git repo name
- An Azure App Service nicknamed “gutentag.”
- Git repo name
hr-payroll-hw-api-gutentag
.
- Git repo name
- An Azure Static Web App nicknamed “ciao.”
- Git repo name
hr-payroll-hw-ui-ciao
- Git repo name
- An Azure RBAC role assignment with:
- a principal of the “bonjour” function app’s SAMI,
- a role of “
Key Vault Reader
,” and - a scope of the Key Vault resource ID,
- because the Key Vault contains connection details to a 3rd-party application that the “bonjour” function needs to be able to query during runtime.
- An Azure RBAC role assignment with
- a principal of the data factory’s SAMI,
- a role of “
Key Vault Reader
,” and - a scope of the Key Vault resource ID,
- because the Key Vault contains connection details to a 3rd-party application that data factory needs to be able to query during runtime.
- An Azure RBAC role assignment with
- a principal of the data factory’s SAMI,
- a role of “
Storage Blob Data Contributor
,” and - a scope of the Storage Blob resource ID,
- because the data factory needs to be able to write files into the storage blob during runtime.
Note the use of system-assigned managed identity, not app registrations, for inter-Azure-resource permissions during runtime.
SAMI is always preferable to user-assigned managed identity or app registrations when it is available.
(It simply happens to not be available in Azure DevOps Pipelines or GitHub Actions at the time this article was written.)
Quantity considerations
Recap:
We’ve got 5 Git repositories (“repos”), each with their own deployment automation “CI/CD pipeline.”
So that’s 5 pipelines.
Each pipeline involves 3 “stages” of deployment (except ADF which only deploys to higher environments, so 2 for that one).
That makes for a total of 14 mini-projects/apps/workloads within the larger “Hello World” application universe.
- Q: Does that mean we need to create 14 Entra App Registrations?
- A: Not necessarily.
14 app registrations
Off the top of my head, when using Azure DevOps Pipelines, I’m not sure I can think of a great reason to have 14 different Entra identities to run your 5 pipelines as.
Maybe it could help you do some typo-accident dummy-checking if you can see the word dev
, stg
, or prd
while writing each stage of each pipeline codebase.
I’m not convinced it would help with malicious tampering, though, since ultimately each pipeline will authenticate to Azure using all (2-)3 of its environment-scoped Entra identities.
If someone can edit the repo, they can just backspace out stg
and type prd
in part of the pipeline definition if they want to make trouble, right?
Feel free to debate this in the comments! I might be missing something important.
With GitHub Actions, I better see the motivation to have 14 different App Registrations. GitHub Actions automatically reaches out to a different “federated credential” at every stage of deployment if you use its “environments” feature as a restriction.
This means that if you’re already protecting your GitHub Actions stages with “environment” restrictions, you’re going to need to create 14 “federated credentials” in Entra anyway just to get things working.
I suppose you might as well consider attaching 1 federated identity apiece to 14 different Entra app registrations and enjoy a bit of accident risk reduction.
14 app registrations might be named something like this:
hr-payroll-hello-world-dev-function-app-bonjour-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thedev
bonjour function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Perhaps, specifically, its
dev
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-dev-function-app-hola-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thedev
hola function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Perhaps, specifically, its
dev
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-dev-app-service-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thedev
gutentag app service resource ID. - Has a federated identity credential for the
hr-payroll-hw-api-gutentag
repo.- (Perhaps, specifically, its
dev
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-dev-ui-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thedev
ciao static web app resource ID. - Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Perhaps, specifically, its
dev
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-stg-data-factory-cicd
- Serves as principal when having 1 “
Data Factory Contributor
” Azure RBAC role assignment created, with a scope of thestg
data factory resource ID. - Has a federated identity credential for the
hr-payroll-hw-adf
repo.- (Perhaps, specifically, its
stg
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-stg-function-app-bonjour-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thestg
bonjour function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Perhaps, specifically, its
stg
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-stg-function-app-hola-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thestg
hola function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Perhaps, specifically, its
stg
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-stg-app-service-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thestg
gutentag app service resource ID. - Has a federated identity credential for the
hr-payroll-hw-api-gutentag
repo.- (Perhaps, specifically, its
stg
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-stg-ui-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of thestg
ciao static web app resource ID. - Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Perhaps, specifically, its
stg
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-prd-data-factory-cicd
- Serves as principal when having 1 “
Data Factory Contributor
” Azure RBAC role assignment created, with a scope of theprd
data factory resource ID. - Has a federated identity credential for the
hr-payroll-hw-adf
repo.- (Perhaps, specifically, its
prd
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-prd-function-app-bonjour-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of theprd
bonjour function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Perhaps, specifically, its
prd
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-prd-function-app-hola-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of theprd
hola function resource ID. - Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Perhaps, specifically, its
prd
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-prd-app-service-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of theprd
gutentag app service resource ID. - Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Perhaps, specifically, its
prd
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
hr-payroll-hello-world-prd-ui-cicd
- Serves as principal when having 1 “
Website Contributor
” Azure RBAC role assignment created, with a scope of theprd
ciao static web app resource ID. - Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Perhaps, specifically, its
prd
flavor, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- (Perhaps, specifically, its
- Serves as principal when having 1 “
5 app registrations
If you have different teams working on each of the 5 repos, I can totally see creating 5 App Registrations:
hr-payroll-hello-world-data-factory-cicd
- Serves as principal when having 2 “
Data Factory Contributor
” Azure RBAC role assignments created, each with a scope to one of the 2 higher-level data factory resource IDs (stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-adf
repo.- (Or perhaps 2, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 2 “
hr-payroll-hello-world-function-app-bonjour-cicd
- Serves as principal when having 3 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 3 bonjour function resource IDs (dev
,stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 3 “
hr-payroll-hello-world-function-app-hola-cicd
- Serves as principal when having 3 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 3 hola function resource IDs (dev
,stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 3 “
hr-payroll-hello-world-app-service-cicd
- Serves as principal when having 3 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 3 gutentag app service resource IDs (dev
,stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-api-gutentag
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 3 “
hr-payroll-hello-world-ui-cicd
- Serves as principal when having 3 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 3 ciao static web app resource IDs (dev
,stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 3 “
That way, a malicious (or careless) front-end developer can’t overwrite a function’s codebase by adding some function code to the hr-payroll-hw-ui-ciao
and edit its deployment automation CI/CD pipeline to deploy their new code onto one of the “hola
” Azure Function App resources.
2 app registrations
Similarly, maybe you’re not worried about accidents or malice between the 4 web apps, and have the same developers working on them all, anyway, and you don’t expect that to ever change. In that case, perhaps you’d want something more like:
hr-payroll-hello-world-data-factory-cicd
- Serves as principal when having 2 “
Data Factory Contributor
” Azure RBAC role assignments created, each with a scope to one of the 2 higher-level data factory resource IDs (stg
,prd
). - Has a federated identity credential for the
hr-payroll-hw-adf
repo.- (Or perhaps 2, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Serves as principal when having 2 “
hr-payroll-hello-world-web-cicd
- Serves as principal when having 12 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 12 web-related resource IDs (3 environments x 4 web-related resources).- (Alternatively, if those 12 resources live within Azure “resource groups” that don’t contain any other Azure resources that a “Website Contributor” role could produce accidents or tampering against, you could scope to those resource groups and create fewer total role assignments.)
- Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-api-gutentag
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Alternatively, if all 4 of your web-related repos are in the same Azure DevOps Project, you could get away with just 1 federated identity credential for a single web-related ADO Service Connection to this 1 web-related Entra app registration.
- Serves as principal when having 12 “
If you’re using GitHub Actions with “environments” and want a wee bit of help reducing opportunities for accidents, but the “14 app registrations” option seems like overkill, perhaps instead of 2 app registrations (-data-factory-cicd
and -web-cicd
), you have 5 (dev-web-cicd
, stg-data-factory-cicd
, stg-web-cicd
, prd-data-factory-cicd
, and prd-web-cicd
).
1 app registration
You could just make 1 app registration called:
hr-payroll-hello-world-cicd
- Serves as principal when having 2 “
Data Factory Contributor
” Azure RBAC role assignments created, each with a scope to one of the 2 higher-level data factory resource IDs (stg
,prd
). - Serves as principal when having 12 “
Website Contributor
” Azure RBAC role assignments created, each with a scope to one of the 12 web-related resource IDs (3 environments x 4 web-related resources).- (Alternatively, if those 12 resources live within Azure “resource groups” that don’t contain any other Azure resources that a “Website Contributor” role could produce accidents or tampering against, you could scope to those resource groups and create fewer total role assignments.)
- Has a federated identity credential for the
hr-payroll-hw-adf
repo.- (Or perhaps 2, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-func-bonjour
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-func-hola
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-api-gutentag
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Has a federated identity credential for the
hr-payroll-hw-ui-ciao
repo.- (Or perhaps 3, if using GitHub Actions and your CI/CD pipeline stages use GitHub “environments.”)
- Alternatively, if all 5 of your repos are in the same Azure DevOps Project, you could get away with just 1 federated identity credential for a single ADO Service Connection to this 1 Entra app registration.
- Serves as principal when having 2 “
If you’re using GitHub Actions with “environments” and want a wee bit of help reducing opportunities for accidents, perhaps instead of 1 app registration (-cicd
), you have 5 (-dev-cicd
, -stg-cicd
, and -prd-cicd
).
Of course, this would let a malicious web developer (or intruder!) write a deployment automation CI/CD pipeline that mucks about with the Data Factory’s definition. Who knows – maybe they’ll…
- Rewrite the Data Factory to email themselves a report of all colleagues’ social security numbers.
- Accidentally break the Data Factory’s configuration and ruin years worth of data integrity.
Lessons learned
It’s up to your team to develop standards that work for you.
I hope this article has helped you better think through the security and ease-of-maintenance implications of your many options as you face one of the “two hardest problems in computer science:” naming things.