Connect to AzureAD with Service Principal

I needed this already multiple times but never got it working. Today, I needed again the ability to Connect to AzureAD with Service Principal because some actions can’t be done (yet) via the Azure Resource Manager.

You can’t login into the Azure AD with a key as a Service Principal. You need a certificate for this. Read for more information the documentation of Connect-AzureAD. In order to use a key for logging into the Azure AD, we need to login first into AzureRM because there it is possible by default. Then call something from the Azure AD (in example a group or application) with AzureRM so the tokencache of the AzureRM context is filled with a valid token to “https://graph.windows.net/”. After that, use the token to login with Connect-AzureAD AadAccessToken cmdlet.

Sample

This script (written in AzureRM 5.1.1 because VSTS hosted agents are using that as well) will get an Azure AD application via AzureRM and then via AzureAD cmdlets.

$ObjectIdOfApplicationToChange = "82bd7dd3-accf-4808-97ef-6bc6e27ade9b"

$TenantId = "You tenant here"
$ApplicationId = "Your application id to login here"
$ServicePrincipalKey = ConvertTo-SecureString -String "Put a key of the application here" -AsPlainText -Force

Write-Information "Login to AzureRM as SP: $ApplicationId"
$AzureADCred = New-Object System.Management.Automation.PSCredential($ApplicationId, $ServicePrincipalKey)
Add-AzureRmAccount -ServicePrincipal -Credential $AzureADCred -TenantId $TenantId

# Get application with AzureRM because this will fill the tokencache for AzureAD as well (hidden feature).
Write-Information "Get application with AzureRM: $ObjectIdOfApplicationToChange"
Get-AzureRmADApplication -ObjectId $ObjectIdOfApplicationToChange

$ctx = Get-AzureRmContext
$cache = $ctx.TokenCache
$cacheItems = $cache.ReadItems()

$token = ($cacheItems | where { $_.Resource -eq "https://graph.windows.net/" })

Write-Information "Login to AzureAD with same SP: $ApplicationId"
Connect-AzureAD -AadAccessToken $token.AccessToken -AccountId $ctx.Account.Id -TenantId $ctx.Tenant.Id

Write-Information "Now get same application with AzureAD: $ObjectIdOfApplicationToChange"
Get-AzureADApplication -ObjectId $ObjectIdOfApplicationToChange

12 thoughts to “Connect to AzureAD with Service Principal”

  1. Hi Ralph,
    I commented on your other blog article (connecting to AzureAD when writing an extension)…
    But this one looks more like what I and many others are trying: Get access to AzureAD in a pipeline.

    I was hoping to use this from an AzureRM Powershell task in AzureDevOps, but I get this error:

    Code: Authentication_MissingOrMalformed
    Message: Access Token missing or malformed.
    RequestId: bbb7688f-822b-440d-af45-18df70afe47b
    DateTimeStamp: Sun, 14 Apr 2019 23:31:56 GMT
    HttpStatusCode: Unauthorized
    HttpStatusDescription: Unauthorized
    HttpResponseStatus: Completed

    If I try your code as myself it works (which is great for my scripts that require both AzureRM and AzureAD cmdlets – I only have to authenticate once). But not when running it in a pipeline.

    My ADO service principal has the App Administrator role.

    It can successfully issue this cmdlet:
    Get-AzureRmADApplication -DisplayNameStartWith “CustomApp”

    But this:
    Get-AzureADApplication -SearchString “CustomApp”
    generates the above error

    1. I’m not sure. Not really done this in pipeline. Did you do this step:
      # Get application with AzureRM because this will fill the tokencache for AzureAD as well

      Did you check the context? $ctx.Account.Id
      Did you check the access token? Is it correct? You can use https://jwt.ms for this

      1. Yes – executed a ‘get-azurermadapplication’ first.

        The token decodes OK

        I outputted the parameters Connect-AzureAD uses to the screen, and ‘accountId’ and ‘tenantId’ are both secure strings (outputting to screen gives literal ‘***’), so I’m not sure if they aren’t being decrypted correctly.

        However I can recognise accountId (as ‘oid’ and ‘sub’) and tenantId (as ‘iss’ and ‘tid’) in the decoded token. So I tried hardcoding those values into the cmdlet, but it is the same result/error.

        UPDATE: I’ve just realised, and confirmed, the task/script fails on Connect-AzureAD (I thought it was failing on the Get-AzureADApplication step)

        1. I don’t know this because I’ve never done this. But one possible reason:
          Are you doing Get-AzureRmADApplication and Connect-AzureAD and Get-AzureADApplication in the same task? Because every task has a new context…

          Have you tried using Connect-AzureAD with saved credentials of a user? Just to confirm that AzureAD module is ok?

  2. Yeah – all in the same task…. not to worry: I’m on holiday from tomorrow, so will park this for a while…

  3. Thanks for this. Nice example. All works until I try and call Get-AzureADUser and I get “Authorization_RequestDenied
    Message: Insufficient privileges to complete the operation.”. I have added API permissions to Microsoft Graph for all user and directory. Any input would be massively appreciated. Thanks.

    1. These are the old Azure AD cmdlets so you must not set the permissions on the Microsoft Graph but on the old Azure AD Graph. Then it should work. The Microsoft graph is new but doesn’t have all the functionality (yet) from the old one.

Leave a Reply