Provisioning Azure AD Service Principals that can deploy built webapps onto your Azure App Service resources
16 Mar 2023
In this series, we’ve written the world’s tiniest webserver, added unit tests to its codebase, told Azure DevOps (“ADO”) Pipelines to auto-build a runnable web server for us each time we update our Git-tracked source code, and rented 2 webserver hosts from Microsoft Azure’s public cloud (one “nonproduction” and one “production”).
Every time ADO builds our source code into a runnable web server, we’ll want it to deploy the “built” codebase onto the hosts we rented. Our ADO automation will need to prove it’s not an evil hacker trying to take over our web hosts. Therefore, we need to:
- Create an Azure Active Directory (“AAD”) identity ADO can use to prove that it is who it says it is.
- To do so, we’ll create a pair of AAD resource instances – one “Application Registration” and one “Service Principal.”
- Create an Azure RBAC role assignment authorizing the new AAD service principal to deploy code onto our web hosts.
- (We’ll worry about telling ADO how to use the new AAD service principal in a subsequent article.)
Prerequisites
- Have an account in Azure.
- Log your computer’s command line into that Azure account.
- Determine the unique name of the Azure resource group that you’d like to create webhost resources within, and make sure it exists.
- For this exercise, I created a brand new one named “
my-hello-web-rg
” inside my Azure account.
- For this exercise, I created a brand new one named “
Note: In an enterprise context, your nonproduction and production webhost resources might end up in two separate Azure resource groups. In that case, you’d likely need to create 2 Azure RBAC role assignments for your AAD Service Principal (one for each resource group).
Luckily, in an enterprise setting, there’s probably another team worrying about this for you who will be provisioning Azure resource groups resources, Azure App Service Service Plan (“AASSP”) resources, AASWA resources, AAD Application Registrations, AAD Service Principals, and Azure RBAC Role Assignments for you.
For the purpose of tutorial simplicity, I am using just 1 resource group, so we’ll only create 1 Azure RBAC Role Assignment.
Provision an Azure AD application registration
In my scripts below, be sure to substitute:
- “
my-first-app-reg
” if that’s not actually the name of the AAD application registration you’d like to create.
With Azure PowerShell
If ( `
Get-AzAdApplication `
-DisplayName "my-first-app-reg" `
) {} `
Else { `
New-AzAdApplication `
-DisplayName "my-first-app-reg"; `
}
With the Azure CLI
In a Windows PowerShell command prompt
If ( `
az ad app list `
--display-name "my-first-app-reg" `
--query "[0]" `
) {} `
Else { `
az ad app create `
--display-name "my-first-app-reg"; `
}
In a Linux-style command prompt
if [ \
$( \
az ad app list \
--display-name "my-first-app-reg" \
--query "[0].id" \
) \
]; \
then
:; \
else
az ad app create \
--display-name "my-first-app-reg"; \
fi;
Provision an Azure AD service principal
In my scripts below, be sure to substitute:
- “
my-first-app-reg
” if that’s not actually the name of the AAD application registration you just created.
With Azure PowerShell
If ( `
Get-AzAdServicePrincipal `
-ApplicationId ( ( `
Get-AzAdApplication `
-Filter "DisplayName eq 'my-first-app-reg'" `
-First 1 `
).AppId ) `
) {} `
Else { `
New-AzAdServicePrincipal `
-ApplicationId ( ( `
Get-AzAdApplication `
-Filter "DisplayName eq 'my-first-app-reg'" `
-First 1 `
).AppId ) `
}
With the Azure CLI
In a Windows PowerShell command prompt
If ( `
az ad sp list `
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName}[0]" `
) {} `
Else { `
az ad sp create `
--id $( `
az ad app list `
--display-name "my-first-app-reg" `
--query "[0].id" `
); `
}
In a Linux-style command prompt
if [ \
$( \
az ad sp list \
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName,id:id}[0].appDisplayName" \
) \
]; \
then
:; \
else
az ad sp create \
--id $( \
az ad app list \
--display-name "my-first-app-reg" \
--query "[0].id" \
); \
fi;
Provision an Azure RBAC role assignment
Services logged into AAD using the “application registration” + “service principal” pair you just created can’t actually do anything useful in Azure unless you give that AAD pairing adequate Azure permissions.
A Microsoft-maintained built-in RBAC role called “Website Contributor” is a great one for CI/CD pipelines that simply need to deploy “built” web server runtimes onto Azure App Services web-hosting resources.
We’ll set up a “role assignment” to link our new AAD resource to the “website contributor” set of Azure permissions.
In my scripts below, be sure to substitute:
- “
my-first-app-reg
” if that’s not actually the name of the AAD application registration you’d like to create. - “
my-hello-web-rg
” if that’s not actually the name of the resource group you created.
With Azure PowerShell
If ( `
Get-AzRoleAssignment `
-ObjectId ( `
Get-AzAdServicePrincipal `
-ApplicationId ( `
Get-AzAdApplication `
-Filter "DisplayName eq 'my-first-app-reg'" `
-First 1 `
).AppId `
).Id `
-Scope ( `
Get-AzResourceGroup `
-Name "my-hello-web-rg" `
).ResourceId `
-RoleDefinitionName "Website Contributor" `
) {} `
Else { `
New-AzRoleAssignment `
-Description "let-service-principal-manage-websites-in-resource-group" `
-ApplicationId ( `
Get-AzAdApplication `
-Filter "DisplayName eq 'my-first-app-reg'" `
-First 1 `
).AppId `
-Scope ( `
Get-AzResourceGroup `
-Name "my-hello-web-rg" `
).ResourceId `
-RoleDefinitionName "Website Contributor" `
-ObjectType "ServicePrincipal" `
}
With the Azure CLI
In a Windows PowerShell command prompt
If ( `
az role assignment list `
--assignee $( `
az ad sp list `
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName,id:id}[0].id" `
) `
--scope $( `
az group show `
--name "my-hello-web-rg" `
--query "id" `
) `
--role "Website Contributor" `
--query "[0]" `
) {} `
Else { `
az role assignment create `
--description "let-service-principal-manage-websites-in-resource-group" `
--assignee-object-id $( `
az ad sp list `
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName,id:id}[0].id" `
) `
--scope $( `
az group show `
--name "my-hello-web-rg" `
--query "id" `
) `
--role "Website Contributor" `
--assignee-principal-type "ServicePrincipal"; `
}
In a Linux-style command prompt
if [ \
$( \
az role assignment list \
--assignee $( \
az ad sp list \
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName,id:id}[0].id" \
| xargs \
) \
--resource-group "my-hello-web-rg" \
--role "Website Contributor" \
--query "[0].id" \
) \
]; \
then
:; \
else
az role assignment create \
--description "let-service-principal-manage-websites-in-resource-group" \
--assignee-object-id $( \
az ad sp list \
--query "[?appDisplayName=='my-first-app-reg'].{appDisplayName:appDisplayName,id:id}[0].id" \
| xargs \
) \
--resource-group "my-hello-web-rg" \
--role "Website Contributor" \
--assignee-principal-type "ServicePrincipal"; \
fi;
Posts in this series
- Part 1 - Source code that builds locally into a Node.js Hello World webapp
- Part 2 - Locally unit-testing source code for a Node.js Hello World webapp
- Part 3 - Protecting Git branches in Azure DevOps repositories
- Part 4 - Making Azure DevOps Pipelines build a Hello World webapp from Git-tracked source code changes
- Part 5 - Failing Azure DevOps Pipeline builds if unit tests fail
- Part 6 - Logging your command line into Azure
- Part 7 - Provisioning an Azure Resource Group
- Part 8 - Provisioning Azure App Services to host your Hello World webapp
- Part 9 - This Article
- Part 10 - Provisioning Azure DevOps Service Connections that let ADO Release Pipelines leverage Azure AD Service Principals for sensitive CI/CD tasks
- Part 11 - Deploying a built webapp onto Azure App Service with ADO Release Pipelines