Author : MD TAREQ HASSAN | Updated : 2021/10/12
What is Service Principal
- An Azure service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources
- Service Principal is the “Security Principal” for an Application/Service/Tool
- A Service principle is a Security Principle for an application mentioned that the application is registered in Azure AD
- In a multi-tenant scenario, application is registered in one tenant but is accessed by users from other tenants
- Therefore seperate indipendent service principle is created in each tenant for that same application (which will be accessed by users corresponding tenants)
- An Azure service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources
- Relation with Application Object
- When an application is registered in Azure AD, Application Object (one and only) is created in the home tenant (the tenant in which application was registered)
- An application object is used as a template or blueprint to create one or more service principal objects
- A service principal is created in every tenant where the application is used
- Similar to a class in object-oriented programming
- Application object is class
- Service Principal is instance of that class (in each tenant in a multi-tenant environment)
- Types of Service Principal: https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals#service-principal-object
From Microsoft Doc.
“An Azure Active Directory (Azure AD) service principal is the local representation of an application object in a single tenant or directory.
It functions as the identity of the application instance. Service principals define who can access the application, and what resources the application can access”
Creating Service Principal
- Service Principal can not exist itself without associated application registration
- When you register an application using the Azure portal, a service principal is created automatically
- Creating Service Principal using PowerShell
- Using “
Az.Resources
” module (recommended)- Login:
Connect-AzAccount
- Create Service Principal
New-AzAdServicePrincipal -DisplayName $servicePrincipalDisplayName
(New-AzAdServicePrincipal -DisplayName $servicePrincipalDisplayName -PasswordCredential $credentials
)- The command will create application registration and service principle with same DisplayName
- Login:
- Using “
AzureAD
” module- Install:
Install-Module AzureAD
(Import-Module AzureAD
) - Login:
Connect-AzureAD
- Create Service Principal
- Create app registration first, get AppId
New-AzureADServicePrincipal -AppId $appId -DisplayName "xxx"
- DisplayName for application registration and service principal must be the same
- You might need to create client secret seperately
- Install:
- Using “
Application secret
- When not using “
-PasswordCredential
” flag (recommended), secret will be generated (with GUID as KeyId for secret) - You can get application secret during creation time only (from the returned object)
Role
- Contributor role will be assigned to service principal at the current subscription by default
- Use overloaded function to assign target role
Create utility class: azure_utility.ps1
#
#
# Azure Utility Class
#
#
class AzureUtility {
static [void] LoginAndSetContext([string] $targetSubscriptionName) {
$azureLoginCredential = Get-Credential -Message "Please Enter Azure Login Credential `r`n(Azure AD Login Id & Password)"
Connect-AzAccount -Credential $azureLoginCredential
Set-AzContext -Subscription (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id
}
static [string] CreateServicePrincipal([string] $servicePrincipalDisplayName) {
$spInfo = ""
#
# app password == client secret -> use as password during login with service principal
# appId == clientId == -> use as username during login with service principal
#
$newSp = (New-AzADServicePrincipal -DisplayName $servicePrincipalDisplayName)
$tenantId = [AzureUtility]::GetCurrentTenantId()
$applicationId = $newSp.ApplicationId # client id / app id
$secret = (ConvertFrom-SecureString $newSp.Secret) # client secret / app password (secure string)
$spInfo = "DisplayName: $servicePrincipalDisplayName`r`nTenantId: $tenantId`r`nApplicationId: $applicationId`r`nSecret: $secret"
return $spInfo
}
}
Create service principal: admin_script.ps1
#
# Define rquired variables
#
$servicePrincipalDisplayName = "this-is-test-sp"
# client secret for service principal will be generated automatically (as SecureString)
# by default "Contributor" role will be assigned to the newly created service principal at current subscription scope (AzContext)
#
# Get script folder
#
$scriptFolder = ""
if ($psISE) { # If using ISE
$scriptFolder = Split-Path -Parent $psISE.CurrentFile.FullPath
} elseif($PSVersionTable.PSVersion.Major -gt 3) { # If Using PowerShell 3 or greater
$scriptFolder = $PSScriptRoot
} else { # If using PowerShell 2 or lower
$scriptFolder = split-path -parent $MyInvocation.MyCommand.Path
}
#
# Load utility class
#
. ($scriptFolder + "\azure_utility.ps1")
$newServicePrincipalCredentials = [AzureUtility]::CreateServicePrincipal($servicePrincipalDisplayName)
#
# Save to file since secret is visible only during service principal creation time
#
$fileUri = "$($scriptFolder)\credential_$($servicePrincipalDisplayName).txt"
echo $newServicePrincipalCredentials > $fileUri
Creating Service Principal with Given Password
- Application secret
- Use “
-PasswordCredential
” flag to provide passwrod credential (New-Object Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential
) - Client secret identifier (KeyId) might look like ‘0000-0000-0000 ….’
- Use “
- Role: You have to assign role manually to the target subscription
Create utility class: azure_utility.ps1
#
#
# Azure Utility Class
#
#
class AzureUtility {
static [string] GeSubscriptionId([string] $subscriptionName) {
return (Get-AzSubscription -SubscriptionName $subscriptionName).Id
}
static [string] GetServicePrincipalObjectId([string] $servicePrincipalDisplayName) {
return (Get-AzADServicePrincipal -DisplayName $servicePrincipalDisplayName).Id
}
static [string] GetServicePrincipalInfo([string] $servicePrincipalDisplayName) {
$spInfo = ""
$servicePrincipal = (Get-AzADServicePrincipal -DisplayName $servicePrincipalDisplayName)
if($servicePrincipal){
$tenantId = [AzureUtility]::GetCurrentTenantId()
$applicationId = $servicePrincipal.ApplicationId # client id
$secret = "<only available during service principle creation time>" # client secret
$spInfo = "DisplayName: $servicePrincipalDisplayName`r`nTenantId: $tenantId`r`nApplicationId: $applicationId`r`nSecret: $secret"
}
return $spInfo
}
static [string] CreateServicePrincipal([string] $servicePrincipalDisplayName, [string] $secret) {
$spInfo = ""
#
# Check if service principal already exists
#
$spInfo = [AzureUtility]::GetServicePrincipalInfo($servicePrincipalDisplayName)
if($spInfo) { # return existing service principal
return $spInfo
}
#else: create new service principal
#
# app password == client secret -> use as password during login with service principal
# appId == clientId == -> use as username during login with service principal
#
$spCredential = New-Object Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential -Property @{ StartDate = (Get-Date); EndDate = (Get-Date -Year 2050); Password = $secret }
$newSp = New-AzADServicePrincipal -DisplayName $servicePrincipalDisplayName -PasswordCredential $spCredential
$tenantId = [AzureUtility]::GetCurrentTenantId()
$applicationId = $newSp.ApplicationId # use as username during login with service principal
$spInfo = "DisplayName: $servicePrincipalDisplayName`r`nTenantId: $tenantId`r`nApplicationId: $applicationId`r`nSecret: $secret"
return $spInfo
}
static [void] AssignRoleToServicePrincipal([string] $servicePrincipalObjectId, [string] $roleDefinitionName, [string] $targetScope) {
New-AzRoleAssignment -ObjectId $servicePrincipalObjectId `
-RoleDefinitionName $roleDefinitionName `
-Scope $targetScope
Write-Host "$roleDefinitionName role has been assigned to $targetScope"
}
}
Create service principal: admin_script.ps1
#
# Define rquired variables
#
$subscriptionName = "xxx"
$servicePrincipalDisplayName = "yyy"
$secret = "zzz" # client secret for service principal
$roleDefinitionName = "Contributor"
#
# Get script folder
#
$scriptFolder = ""
if ($psISE) { # If using ISE
$scriptFolder = Split-Path -Parent $psISE.CurrentFile.FullPath
} elseif($PSVersionTable.PSVersion.Major -gt 3) { # If Using PowerShell 3 or greater
$scriptFolder = $PSScriptRoot
} else { # If using PowerShell 2 or lower
$scriptFolder = split-path -parent $MyInvocation.MyCommand.Path
}
#
# Load utility class
#
. ($scriptFolder + "\azure_utility.ps1")
#
# Create service principal
#
$newSpInfo = [AzureUtility]::CreateServicePrincipal($servicePrincipalDisplayName, $secret)
#Write-Host $newSpInfo
#
# Get service principal object id
#
$servicePrincipalObjectId = [AzureUtility]::GetServicePrincipalObjectId($servicePrincipalDisplayName)
#
# Assign role
#
# Resource Scope Format: /subscriptions/<subscriptionId>/resourcegroups/<resourceGroupName>/providers/<providerName>/<resourceType>/<resourceSubType>/<resourceName>
# For subscription: /subscriptions/<subscriptionId
#
$subscriptionId = [AzureUtility]::GeSubscriptionId($subscriptionName)
$subscriptionScope = "/subscriptions/$subscriptionId"
[AzureUtility]::AssignRoleToServicePrincipal($servicePrincipalObjectId, $roleDefinitionName, $subscriptionScope)
Login Using Service Principal
$tenantId = "xxx"
$appId = "yyy"
$appSecret = "zzz"
$appSecretAsSecureString = (ConvertTo-SecureString $appSecret)
$credential = (New-Object -TypeName PSCredential -ArgumentList $appId, $appSecretAsSecureString) #$credential = Get-Credential
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $tenantId
#
# Check service principal login is successful
#
Get-AzContext
Azure CLI
az login --service-principal -u <app-id> -p <password-or-cert> --tenant <tenant>
Links
- About Service Principal:
- Creating Service Principal:
- Relationship between application objects and service principals : https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals#relationship-between-application-objects-and-service-principals