VSTS Build and PowerShell and AzureAD Authentication - powershell

I have a VSTS project connected via a Service Principal to an Azure subscription through an Azure Resource Manager endpoint. This works fine for my builds that configure ARM resources via templated, parameter driven deployments.
I have an additional requirement to set up Azure AD groups as part of the build. I have a script that works fine from my local machine. When I deployed it via the build and it executed on the hosted build controller, the script could initially not find the AzureAD module. I got around this by including the script in git Repo and accessing it through:
$adModulePath = $PSScriptRoot + "\PsModules\AzureAD\2.0.0.131\AzureAD.psd1"
Import-Module $adModulePath
However, I now have another problem when it comes to running New-AzureADGroup. The script requires Connect-AzureAD to be run before the command is issued. This works fine by hardcoding a credential but I don't want to do this, I want it to run under the context of the SPN created, which is running the scripts on the hosted build controller.
So, the question is, can I get the current context of the Azure PowerShell execution SPN and pass that to Connect-AzureAD to avoid storing credential in plain text? Am I missing a trick? Are there any alternatives?
My current code is as below, the commented connection works fine from the command like with hard coded values. The call with no parameters presents the login UI which terminates the build since it is obviously not interactive.
## Login to Azure
#$SecurePassword = ConvertTo-SecureString $AdminPassword -AsPlainText -Force
#$AdminCredential = New-Object System.Management.Automation.PSCredential ($AdminUserEmailAddress, $SecurePassword)
#Connect-AzureAD -Credential $AdminCredential
Connect-AzureAD
Write-Output "------------------ Start: Group Creation ------------------"
$TestForAdminGroup = Get-AzureADGroup -SearchString $AdminGroup
$TestForContributorGroup = Get-AzureADGroup -SearchString $ContributorGroup
$TestForReaderGroup = Get-AzureADGroup -SearchString $ReaderGroup
Thanks

This is possible. Got it working today for my own VSTS extension that I released a while ago. My extension is using a Azure Resource Manager endpoint as input.
Running it now on a Microsoft Hosted Visual Studio 2017 agent pool using the below code. See for more information my post on how to use AzureAD PowerShell cmdlets on VSTS agent.
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

To conclude, the Powershell module can’t share the same context and you need to store the credential in secret variable in VSTS.

To take this further, it is possible to use the Service Principal by following example 3 here:
https://learn.microsoft.com/en-us/powershell/module/azuread/connect-azuread?view=azureadps-2.0
Once you have created a self-signed cert and attached it, you can connect to the Azure AD by passing in the thumbprint of the cert along with a couple of other parameters:
Connect-AzureAD -TenantId $tenantId -ApplicationId $sp.AppId -CertificateThumbprint $thumb

Related

Authentication_MissingOrMalformed when executing New-AzureADApplication

I'm trying to run this script but I get this error :
New-AzureADApplication : Error occurred while executing NewApplication
Code: Authentication_MissingOrMalformed
Message: Access Token missing or malformed.
Import-Module AzureAD  -Force
$rmAccount = Add-AzureRmAccount
$subscriptionId = $rmAccount.Context.Subscription.Id
$tenantId = $rmAccount.Context.Tenant.Id
$tokenCache = $rmAccount.Context.TokenCache
$cachedTokens = $tokenCache.ReadItems() `
| where { $_.TenantId -eq $tenantId } `
| Sort-Object -Property ExpiresOn -Descending
$infos = Connect-AzureAD -TenantId $tenantId `
-AadAccessToken $cachedTokens[0].AccessToken `
-AccountId $rmAccount.Context.Account.Id
$clientAadApplication = New-AzureADApplication -DisplayName "TodoListClient-NativeDotNet" `
-ReplyUrls "https://TodoListClient-NativeDotNet" `
-PublicClient $True
$currentAppId = $clientAadApplication.AppId
$clientServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}
$currentAppId = $clientAadApplication.AppIds
What I'm trying to do is to automatically register a native application in Azure Active Directory without dependency to Azure portal so I logged in using Add-AzureRmAccount to get TenantId and SubscriptionId then I used the cached token to connect to AzureAD to prevent double login.
The token that you obtain when you run Add-AzureRmAccount is for the https://management.core.windows.net audience, but Azure AD cmdlets need a token for Azure AD Graph audience (https://graph.windows.net). So you can't reuse that token while calling New-AzureADApplication. You should choose between Azure RM or Azure AD cmdlets, but not both. But as far as I know New-AzureRmADApplication doesn't support creating a native application, so then you should use only Azure AD cmdlets.
You were close, but the token you are passing from the AzureRMAccount is not the correct Audience/Resource for the actions you are doing. Just removing that bit and running something like this below will work with the correct Audience and Resource permissions for your Token. You can always check your Token Audience and scope by copying and pasting it at https://jwt.ms (a useful Token debugger by Microsoft).
Import-Module AzureAD -Force
Connect-AzureAD
$clientAadApplication = New-AzureADApplication -DisplayName "TodoListClient-NativeDotNet" `
-ReplyUrls "https://TodoListClient-NativeDotNet" `
-PublicClient $True
$currentAppId = $clientAadApplication.AppId
$clientServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}
$currentAppId = $clientAadApplication.AppIds

Azure credentials have not been set up or have expired

I have scheduled a PowerShell script to execute a pipeline in Azure.
Have generated the login script(ProfileContext.ctx) using Login-AzureRMAccount
Below is the code to schedule:
$path = "D:\ProfileContext.ctx"
Import-AzureRmContext -Path $path
$dfn = "salesprod"
$rgn = "sale-dw"
$df=Get-AzureRmDataFactory -ResourceGroupName $rgn -Name $dfn
$ISTstartdate = get-date
#Set Pipeline active period
$UTCstartdate = $ISTstartdate.touniversaltime().addminutes(5)
$UTCenddt = $UTCstartdate.AddHours(5)
$pipelinename = "SalesPipeline"
Set-AzureRmDataFactoryPipelineActivePeriod -ResourceGroupName $rgn -PipelineName $pipelinename -DataFactoryName $dfn -StartDateTime $UTCstartdate -EndDateTime $UTCenddt -Force
Above code works fine for 2 or 3 days but then I start getting below issue:
Your Azure credentials have not been set up or have expired, please run Login-AzureRMAccount to set up your Azure credentials.
At D:\RunPipeline.ps1
Below are the version nos:
PSVersion - 5.0.10586.117
Azure - 4.2.1
I resolved it by using below work around: This would auto login for me and then I can schedule without a context file:
$accountName = "pqr#xyz.com"
$password = ConvertTo-SecureString "mypwd" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($accountName, $password)
Login-AzureRmAccount -Credential $credential
Get-AzureRmResource
Add Joseph's answer. Your answer only works on Azure AD account, Microsoft account does not support non-interactive login. According to your scenario, I suggest you could use Service Principal, it is more safer and not leak your account information. What is Service Principal?
When you have an app or script that needs to access resources, you can
set up an identity for the app and authenticate the app with its own
credentials. This identity is known as a service principal.
You could refer to refer to this link to create a new service principal and give Contributor role. You could use the following command to login your subscription.
$subscriptionId=""
$tenantid=""
$clientid=""
$password=""
$userPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientid, $userPassword
Add-AzureRmAccount -TenantId $tenantid -ServicePrincipal -SubscriptionId $subscriptionId -Credential $userCredential
I came across with same issue today but after update Azure PowerShell Modules to the latest version, all good. You can do this while easily using Update-Module or just go ahead and use Install-Module AzureRm -Force.

Powershell Error Open Edx deployment on Azure

Why doest this script return diff error on Windows10 and Mac for the same Powershell Version?
Script executed:
Now, Azure PowerShell on Mac is in preview. It does not have full functions of Windows PowerShell. Parameter ServicePrincipalName is not supported on Mac now, so you get the error.
According to the error on Windows PowerShell. It seems that your application ID or password wrong. Do you check your them? You could use the following cmdlets to create service principal and logon Azure.
#login Azure
Login-AzureRmAccount
#create RM AD application
$app = New-AzureRmADApplication –DisplayName "<Your Application Display Name>" –HomePage "<http://YourApplicationHomePage>" –IdentifierUris "<http://YourApplicationUri>" –Password "<Your Password>"
#create a service principal for that application
New-AzureRmADServicePrincipal –ApplicationId $app.ApplicationId
#Assign that service principal a role.
New-AzureRmRoleAssignment –RoleDefinitionName Reader –ServicePrincipalName $app.ApplicationId
#Authenticating using a service principal
$pass = ConvertTo-SecureString "<Your Password>" -AsPlainText –Force
$cred = New-Object -TypeName pscredential –ArgumentList "<Your UserName>", $pass
Login-AzureRmAccount -Credential $cred -ServicePrincipal –TenantId <Your TenantId>
More information please refer to this link.
I found it. finally thanks to the collaboration of Microsoft support:
this line of code.
Set-AzureSubscription -SubscriptionName $AzureSubscriptionName| Out-Null
Is not correct. It should be:
Select-AzureRmSubscription -SubscriptionName $AzureSubscriptionName| Out-Null

Azure Credentials for build server

I'm having authentication headaches with Azure. I have a continuous build server running powershell scripts, and I'm getting messages like:
Your Azure credentials have not been set up or have expired, please run Login-AzureRMAccount to set up your Azure credentials.
I don't like having to login with my account on the build server. I suppose I could create another account just for building, but that will expire as well. Is there a better way of handling this?
You should not use Login-AzureRmAccount in your build/deploy scripts because this is an interactive login, but rather Add-AzureRmAccount instead.
Add-AzureRmAccount requires you create a service principal (application) in Azure AD and use its client id and secret/key to authenticate.
Here is a code snippet that you can use:
$clientID = "<the client id of your AD Application>"
$key = "<the key of your AD Application>"
$SecurePassword = $key | ConvertTo-SecureString -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential `
-argumentlist $clientID, $SecurePassword
Add-AzureRmAccount -Credential $cred -Tenant "xxxx-xxxx-xxxx-xxxx" -ServicePrincipal
To find out you tenant id (single subscription)
$tenant = (Get-AzureRmSubscription).TenantId
Find tenant id (multiple subscriptions)
$tenant = (Get-AzureRmSubscription -SubscriptionName "My Subscription").TenantId

Azure Powershell programmatic login

I am working with Azure (HDInsight in particular) using a personal account (no work/school acocunt).
I would create a script that automatically login on azure and perform some actions.
I found a solutions saving an azure publishsetting json file after logging with our credentials but this settings file contains token that expires.
How can I deal with this issue? What is the best way to accomplish this automatico logon?
Thanks
Roberto
You need to create a service principal. Once you've created the service principal you can assign it permissions on specific resources using Role-Based Access Control. From there your script can login as the service principal without requiring you to login interactively.
The main concern with this approach is securing access to your script since it contains credentials that allow access to your Azure resources.
This article has a good walkthrough:
#First, login as yourself so you can setup the service principal
Login-AzureRmAccount
#Password doesn't have to be *your* password, but the password the script will use
$app = New-AzureRmADApplication –DisplayName "<Your script name>" –HomePage "http://localhost" –IdentifierUris "http://localhost/YourAppName" –Password "<Password>"
#Create the service principal
New-AzureRmADServicePrincipal –ApplicationId $app.ApplicationId
#Assign the Reader role to your new service principal. Other roles listed at
#https://azure.microsoft.com/en-us/documentation/articles/role-based-access-built-in-roles/
New-AzureRmRoleAssignment –RoleDefinitionName Reader –ServicePrincipalName $app.ApplicationId
$pass = ConvertTo-SecureString "<Password>" -AsPlainText –Force
#Servce principal username looks like 92c22f1f-d1d4-46a1-b025-edb47fc03809#something.onmicrosoft.com
#the GUID part is $app.ApplicationId and the domain part is found in the Azure portal
$cred = New-Object -TypeName pscredential –ArgumentList "<Service Principal UserName>", $pass
Login-AzureRmAccount -Credential $cred -ServicePrincipal –TenantId <TenantId>
If it is not a production/shared setup and more a developer setup you can also do, careful, the password is plain text here:
$SubscriptionName = 'MySubscription'
$pswd = 'MyPassword' | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Credential -UserName 'MyEmail#something.com' -Password $pswd
Add-AzureRmAccount -Credential $creds
Set-AzureRmContext -SubscriptionName $SubscriptionName
Login-AzureRmAccount -Credential $creds -SubscriptionName $SubscriptionName
Below information might help you
Create an Automation Account in Azure
Add your credentials in Automation Account as a variable ( e.g
variablename = loginazure) Below Script will automatically login into azure (use Powershell workflow runbook).
$AzureLogin = Get-AutomationPSCredential -Name 'loginazure'
$AzurePortalLogin = New-Object -TypeName System.Management.Automation.PSCredential$AzureLogin
Add-AzureRmAccount -Credential $AzurePortalLogin
Get-AzureRmSubscription -SubscriptionName "your subscription name" | Set-AzureRmContext
use the above script within Inline Script {}
Regards
Thamarai Selvan S
Here are a couple of commands that you can fire up to get started.
$credentials = Get-Credential
Login-AzureRmAcoount -Credential $credentials
$SubscriptionName
Select-AzureRmSubscription -SubscriptionName "The name of your subscription"
Select-AzureRmSubscription -SubscriptionName $SubscriptionName