Creating Service Principal and New Roles Using AzureRM Powershell
I started working on a few scripts to provision new roles using Azure Resource Manager (ARM). While the documentation online is pretty complete for the Powershell I had a couple of points of consideration: running as a part of an automated action and code versus config.
When I first developed the couple scripts to create the new roles I was just running them from my own command line and logging in myself when prompted. Once I decided that they functionally worked well enough I started looking at how to automate the execution. What I came to discover is the flow is actually in two parts. One part to establish the security artifacts needed to execute and the second part to actually do the role creation.
To get things read in order to run the scripts via some automation, I had to setup all of the artifacts needed for security. That flow looks like:
1. Create an Azure Application
2. Associate a cert with Application
3. Create Service Principal for the Application
4. Assign Owner, or privileged, role to the Service Principal
The documentation was a big help to this end, the documentation for creating a Service Principal presents one with two options: using a password or using a certificate. While the documentation for the ARM Powershell is good, knowing which one to use is another matter.
If you're planning on a temporary solution then the password mechanism might be fine. However, keep in mind that it means you will need to use a bit of Powershell to login (that's hands on keys) and save the token that is returned for future use; that token has an expiry.
Since I wanted something that could theoretically work in perpetuity, or at least until the certificate expires, I went with creating a Service Principal with a certificate. My example isn't exactly ready for the enterprise as I'm still creating it and storing it locally in the My store. However, I've at least parameterized the inputs for cert location. I put the following Powershell into a file and run it myself from the Powershell Console:
# parse params [app name], [uri], [cert store location], [assignedScope]
[string]$appName= "[yourAppName]",#$(throw "Please specify -appName"),
[string]$uri= "[https://www.you.org]",#$(throw "Please specify -uri"),
[string] $certStore= "cert:\CurrentUser\My", #defaulting to CurrentUser,but will not want it in this location for service execution
[string] $assignedScope= "[subscription ID]" #defaulting to my own to ease debug
#to use the same principal on another machine, the cert willhave to exported from the
#machine on which it was created and imported on the targetmachine
#must login manually to perform the rest of the script
Login-AzureRmAccount -SubscriptionId $assignedScope
$appName = "CN=" + $appName
#create self-signed certificate
#for a real-world scenario, this will require an authorityissued cert installed in the preferred
# location (e.g., root)
$cert = New-SelfSignedCertificate -CertStoreLocation $certStore -Subject $appName -KeySpec KeyExchange
#fetch just the keyvalue
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())
#create AD App with key value
$azureAdApplication = New-AzureRmADApplication -DisplayName $appName -HomePage $uri -IdentifierUris $uri -KeyValue $keyValue -KeyType AsymmetricX509Cert -EndDate $cert.NotAfter -StartDate $cert.NotBefore
#compensating transaction for my testing
#Remove-AzureRmADApplication -ApplicationObjectId "[id]"
#Dump application info to screen
#create service principal for application
New-AzureRmADServicePrincipal -ApplicationId $azureAdApplication.ApplicationId
#the principal is created asynchronously, just going to wait
Start-Sleep -s 10
#add Owner role to service principal so it can be used goingforward for provisioning
#the -Scope argument for New-AzureRmRoleAssignmentexpects the format "/subscriptions/<id>"
$subscriptionString = "/subscriptions/"+ $assignedScope
New-AzureRmRoleAssignment -RoleDefinitionName Owner -ServicePrincipalName $azureAdApplication.ApplicationId.Guid -Scope $subscriptionString
One thing to note is that the Subscription ID that I've assigned to $assignedScope works as is in the Login call, but for the New-AzureRMRoleAssignment call it must be prefixed with "/subscriptions/". This caused me a bit of trouble as I got errors telling me that I had no subscription. It didn't inform me that my -Scope argument format was not good.
At the end of it, I have my Service Principal and cert installed and I’m ready to the run scripts using that Principal as my identity.