For some reason it’s not straight-forward to create new credentials for an existing Service Principal account in Azure Active Directory using PowerShell.
I’m using PowerShell, because I’m not an Azure AD admin in my current organization, but as a developer, I am able to create and manage service principal accounts. This is extremely convenient, because we use them for automated deployments to Azure.
We started using Azure DevOps release management about a year ago, and thus I recently encountered the first credential expiration of a service principal that was used by Azure DevOps to deploy resources to Azure. This makes sense, because service principal credential lifetime defaults to one year.
A deployment which worked earlier today just failed with the error message
AADSTS7000222: The provided client secret keys are expired.
Checking the Service connection in Azure DevOps showed the same error:
OK, so just create new credentials, and then update the Service Connection in Azure DevOps.
But that’s not as easy as I would like it to be.
Luckily, finding the Service Principal is easy. The Service Connection window in Azure DevOps (the screenshot above) contains the Service Principal’s “Application ID”. Now, it’s not called that in the screenshot, because the Application ID, Client ID, and many other names mean the same thing when talking about Azure AD.
But take the “Service principal client ID” from the above window, and run this PowerShell command:
Get-AzADServicePrincipal -ApplicationId <service principal client ID> | Get-AzADServicePrincipalCredential
That returns the details about the ServicePrincipals credentials:
StartDate EndDate KeyId Type
--------- ------- ----- ----
29-08-2018 11:33:21 29-08-2019 11:33:21 64677c9e-2dae-4cbf-a031-1152814b3053 Password
Alright, so it expired a few days ago. Then let’s create a new one.
First get the Service Principal into a PowerShell object:
$sp = Get-AzADServicePrincipal -ApplicationId d33bc064-f562-4a7c-99db-643aca6a86c0
Then create new credentials:
$newcredential = New-AzADSpCredential -ServicePrincipalObject $sp
This creates a new password for the service principal, but you’ll never know the password, because it’s secret.
$newcredential
Just returns:
Secret : System.Security.SecureString
StartDate : 30-08-2019 13:07:57
EndDate : 30-08-2020 13:07:57
KeyId : 34e1c9a4-519d-4930-b2fe-1b90ba450554
Type : Password
And the above command to list the credentials shows that a new credential has been created:
StartDate EndDate KeyId Type
--------- ------- ----- ----
30-08-2019 13:07:57 30-08-2020 13:07:57 34e1c9a4-519d-4930-b2fe-1b90ba450554 Password
29-08-2018 11:33:21 29-08-2019 11:33:21 64677c9e-2dae-4cbf-a031-1152814b3053 Password
So $newcredential
contains the new password, but since it’s secret how do you get to it?
There’s a reason that the .NET SecureString
type is unpopular with the security community. You can get to it with two lines of .NET code. And with PowerShell you can just write them at the prompt:
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($newcredential.Secret)
$UnsecureSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
At this point, $UnsecureSecret
contains the clear-text password.
Paste the password into the Update Service Connection window in Azure DevOps, hit the Verify link, and then save it.
Now you have updated the Service Principal credentials that your Azure DevOps Service Connection uses.
In short:
- Get the Application ID from the “Update Service Connection” window’s “Service principal client ID” field.
- Run this in a PowerShell prompt where you have the Az module and you are signed in to Azure:
$sp = Get-AzADServicePrincipal -ApplicationId <your application ID>
$newcredential = New-AzADSpCredential -ServicePrincipalObject $sp
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($newcredential.Secret)
$UnsecureSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
- Output
$UnsecureSecret
and copy/paste it to the “Service Princpal Key” field in Azure DevOps
Does not work with Powershell Core. You only get the first character of the password.
$sp = Get-AzADServicePrincipal -ApplicationId
$newcredential = New-AzADSpCredential -ServicePrincipalObject $sp
$UnsecureSecret = ConvertFrom-SecureString -SecureString $newCredential.Secret -AsPlainText
This works for me.