Use AzureAD PowerShell cmdlets on VSTS agent

Today, I continued working on my custom VSTS extension that I will publish in the near future. In the extension I needed a way how to use AzureAD PowerShell cmdlets on VSTS agent because it isn’t installed by default.

Ship cmdlets in extension

Installing AzureAD cmdlets is not really that difficult. You just download the AzureAD cmdlets module from the PowerShell gallery and put them in your extension so they will be installed with your extension.

Importing the cmdlets

After you put the cmdlets as artifact in your extension, you need to import them. You can do this in example in your execution PowerShell file (in my case Main.ps1) with the following code:

Write-Verbose "Import AzureAD module because is not on default VSTS agent"
$azureAdModulePath = $PSScriptRoot + "\AzureAD\2.0.1.16\AzureAD.psd1"
Import-Module $azureAdModulePath

Connect-AzureAD

After imported, you can use them. Of course you first need to login. Because we don’t want to hard code credentials in our extension, you will have to pass them to the Connect-AzureAD cmdlet. There are multiple ways on how to do this. Think in example of a credential file as variable. I used an Azure Resource Manager endpoint for this. I did this because I actually only wanted to use the AzureRM module for my extension but some of the cmdlets are only in the AzureAD module.

So how do we login? Connect-AzureAD doesn’t allow to login with a Service Principal and a key. You need to use a self-signed certificate for this what I don’t want. I already have an Azure Resource Manager endpoint with a key. I want to use that key so the login procedures for AzureRM and AzureAD are the same. I already wrote a post on how to login with a SP and key but with an existing Azure Resource Manager endpoint in your task you can use the following code:

Write-Verbose "Import AzureAD module because is not on default VSTS agent"
$azureAdModulePath = $PSScriptRoot + "\AzureAD\2.0.1.16\AzureAD.psd1"
Import-Module $azureAdModulePath 

# Workaround to use AzureAD in this task. Get an access token and call Connect-AzureAD
$serviceNameInput = Get-VstsInput -Name ConnectedServiceNameSelector -Require
$serviceName = Get-VstsInput -Name $serviceNameInput -Require
$endPointRM = Get-VstsEndpoint -Name $serviceName -Require

$clientId = $endPointRM.Auth.Parameters.ServicePrincipalId
$clientSecret = $endPointRM.Auth.Parameters.ServicePrincipalKey
$tenantId = $endPointRM.Auth.Parameters.TenantId

$adTokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token"
$resource = "https://graph.windows.net/"

$body = @{
    grant_type    = "client_credentials"
    client_id     = $clientId
    client_secret = $clientSecret
    resource      = $resource
}

$response = Invoke-RestMethod -Method 'Post' -Uri $adTokenUrl -ContentType "application/x-www-form-urlencoded" -Body $body
$token = $response.access_token

Write-Verbose "Login to AzureAD with same application as endpoint"
Connect-AzureAD -AadAccessToken $token -AccountId $clientId -TenantId $tenantId

After the above code, you can run any cmdlet that you want (if your AzureRM endpoint SP has permission on it).

13 thoughts to “Use AzureAD PowerShell cmdlets on VSTS agent”

  1. How do you get the ball rolling?

    I installed the Azure AD Application Management extension. As this doesn’t make the PowerShell-module available, I obviously need to load it somehow. Any suggestions on that?

    1. Hi Jari, this post is not explaining how to use the “Azure AD Application Management” extension. This is more for developers that are creating extensions for Azure DevOps (VSTS). I explain here how to embed it into your extension.

      What are you trying to do? Are you creating an extension?

  2. Hi Ralph,

    Nice article, however is this also possible from a hosted VS2017 agent ? I receive below error:

    ##[error]The term ‘Get-VstsInput’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

    I am using the Azure PowerShell script: InlineScript step
    Thanks

    1. This article is created for people that are creating PowerShell Pipelines extensions for Azure DevOps. So the command ‘Get-VstsInput’ is from the Azure DevOps SDK that you can use in your extensions to get the values from the input fields of a task. So it makes sense that I won’t work immediately with a native Azure PowerShell task.

  3. Hi Ralph,
    Almost exactly what I was after…
    Then I read Dani’s comments. If I read it right the cmdlet get-vstsinput isn’t available on a Hosted Agent. But I’m guessing the information should be there to still use this…?
    Do you think it’s possible? Not having native access to the AzureAD module in AzureDevOps pipelines has been ok up to now (the *-AzureRmAd* cmdlets have been sufficient). But now I’m trying to automate custom App Roles, which are only exposed by AzureAD cmdlets.
    Any tips appreciated.
    Cheers
    Scott

    1. Seems you’re traversing the same path I once did. In my project, there was a need to fiddle with custom Enterprise App roles from Powershell.

      Since the blog post isn’t about what you’re doing, please go see the real answer at https://stackoverflow.com/questions/46554136/vsts-build-and-powershell-and-azuread-authentication

      As a final comment: I spent days on this. There is no way avoiding Connect-AzureAD and going with *-AzureADDirectory* cmdlets. Doing all this inside an Azure DevOps task is possible, but tricky. Ultimately I chose NOT to change the Azure DevOps service connection principal password, but add X.509 certificate to AD service principal and use that for my Powershell scripts. Maybe I should publish that solution in Stackoverflow.

    2. Well the commands are from the Azure DevOps SDK. It’s still a Powershell module so I think it’s possible to run download and install it in a powershell task. https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/powershell/Docs/Consuming.md
      Then you have access to the commands. But it is a workaround to get this all working. I’m thinking that Microsoft will release in the near future a better approach. They making good progress on the v2 endpoint and managing AD all together.

      For the time being were indeed stuck with AzureAD cmdlets. Have you seen my extension for managing applications?
      https://marketplace.visualstudio.com/items?itemName=RalphJansen.Azure-AD-Application-Management
      There is an issue open for managing app roles. If you can confirm the Powershell in this issue:
      https://github.com/LockTar/AzureAdApplicationManagement/issues/24
      then I’m willing to update the extension and create a task for it. Maybe then, you have it working tomorrow ;-)

      1. While traversing the same path, you’re also making the same mistakes I did. :-)

        Yes. You can do all that. Then you will find out, that the API-endpoints are available only INSIDE an extension. As I had a regular Powershell task, no avail.

        If you’re not writing an own extension like Ralph did when he wrote the blog post, you’re looking at wrong material. Also, if you’re not writing an own extension, you won’t need Azure SDK at all. While running your release pipeline task, you can do all kinds of downloading just to find out that a simple:
        > Install-Module -Name AzureAD -Scope CurrentUser -Force
        will solve the problem for you.

        Still, I urge you to follow the Stackoverflow-discussion, since it is not about writing extensions to Azure DevOps, it’s about what you want to be doing.

        1. Ok so just download azuread module and connect via sp that you configured (secret) in the pipeline so you can authenticate.
          But adding the logic to my (our) extension would be better because then we help each other right ;-)

  4. Hi Ralph,

    I found the details of the script very useful. but the permissions on the Graph for application level consent is some thing confusing. If we give permissions for Graph to read and write user groups .It still check for the SPN level permissions on the AAD tennant . Could you clarify on this please with regards to the permissions.

    Thanks
    vishnu

    1. I don’t really understand your question. I think you set the permissions for the Microsoft Graph but you need to set the permissions to the old Azure AD graph.

Leave a Reply