Authentication_MissingOrMalformed when executing New-AzureADApplication - powershell

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

Related

Azure Automation | Runbook | Powershell | Get-AzRoleAssignment | Microsoft.Rest.Azure.CloudException

I have an Automation account and I have set up the Run-As-Account for non-classic resources. In my automation Account I have imported Az.Resources, Az.Account, Az.Storage and Az.KeyVault.
I have a script that does not work under the Automation service principle. The following error is a first of 3;
Get-AzRoleAssignment : Exception of type 'Microsoft.Rest.Azure.CloudException' was thrown. At line:26 char:10
I have granted the application registration the following set of application api permissions in Azure Active Directory (more than I anticipate needing);
At the start of the script I run the Connect-AzAccount cmdlet;
$servicePrincipalConnection = Get-AutomationConnection -Name 'AzureRunAsConnection'
Connect-AzAccount -ServicePrincipal `
-Tenant $servicePrincipalConnection.TenantID `
-ApplicationId $servicePrincipalConnection.ApplicationID `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
Followed by the failing command;
If(!(Get-AzRoleAssignment | Where-Object { $_.RoleDefinitionName -eq "Storage Blob Data Contributor" `
-and $_.scope -eq "/subscriptions/$subscription/resourceGroups/$resourceGroup/providers/Microsoft.Storage/storageAccounts/$serviceName" `
-and $_.SignInName -eq (Get-AzContext).Account.Id})){
# There does not exist the requisite permission for the run-as-account context, grant; 'Storage Blob Data Contributor'.
New-AzRoleAssignment -RoleDefinitionName "Storage Blob Data Contributor" `
-ApplicationId (Get-AzADServicePrincipal | Where-Object { $_.DisplayName -eq "jupiterautomation01" }).ApplicationId `
-Scope "/subscriptions/$subscription/resourceGroups/$resourceGroup/providers/Microsoft.Storage/storageAccounts/$serviceName"
}
I need to grant the right permissions, but do not know what they are.
The permissions assigned need to be consented to by an 'admin'. The button was only available when accessing Azure via the Microsoft account used to create the Active Directory tenant.
Sign in with an admin account that can consent.
Grant API permissions to read or read/write (i needed write as well) on Active Directory to the application.
Give admin consent using the button displayed in the image.

Azure Automation Runbooks, Connect-AzAccount, Assigning rights

I am trying to do some basic group management using Azure Automation, but I'm having a heck of a time getting the script to authenticate correctly.
I have added the Azure.Account modules to the runbook, and the connection seems to get established (at least, it doesn't throw an exception, and the returned object is not null).
When using "Get-AzAdGroup", I am getting:
Get-AzADGroup : Insufficient privileges to complete the operation.
The app account created is a "Contributor" in AAD, so as far as I understand, has full rights to the directory.
I have tried the solution listed at How to connect-azaccount in Azure DevOps release pipeline, to the same effect (Insufficient privileges). I have also applied "Group.Read.All", "Group.ReadWrite.All", "GroupMember.Read.All", "GroupMember.ReadWrite.All" based on what I can read from https://learn.microsoft.com/en-us/graph/permissions-reference#group-permissions - but I'm not 100% clear if the Az* cmdlets use the Microsoft Graph, or if that's separate altogether.
Code is as follows:
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
<#
# Original, technically legacy.
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
#>
$connectState = Connect-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
<#
# From https://stackoverflow.com/questions/56350960/how-to-connect-azaccount-in-azure-devops-release-pipeline, same result.
$AzurePassword = ConvertTo-SecureString "*****" -AsPlainText -force
$psCred = New-Object System.Management.Automation.PSCredential($servicePrincipalConnection.ApplicationId , $AzurePassword)
$connectState = Connect-AzAccount -Credential $psCred -TenantId $servicePrincipalConnection.TenantId -ServicePrincipal
#>
if ($connectState) {
"Connected."
} else {
"Doesn't seem to be connected."
}
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
# Get groups
Get-AzADGroup
My gut tells me that since both connect-azaccount methods yield the same result (connected, but no access) my issue isn't necessarily in the script, but short of creating a service account (which presents challenges with MFA), I don't know how to fix this.
From the solution How to connect-azaccount in Azure DevOps release pipeline I provided, in the screenshot, it is clear that you need to add the API permission of Azure Active Directory Graph, not Microsoft Graph.
Please add the Directory.Read.All in Azure Active Directory Graph for the AD App of your automation run as account.
I have faced a very similar issue. You have a problem with the API permissions Azure APP has.
In my case, my azure App was working as a Service Principal, and not only modifying some stuff in the Azure AD, but also some Azure resources, therefore, these were the api permissions that I had to grant:
Remember that you also need to grant admin consent from the Azure tenant for these permissions update.
If you just assign Contributor role to the service principal, you just can use the sp to get Azure resource(such as VM, app service). So if you want to use the sp tp get Azure AD resource, we need to assign Azure AD role (sucah as Directory Readers) to the sp. For more details, please refer to the document and the document
The detailed steps are as below
Get the RunAsAccount sp object id
Confugure Permisisons for the application
connect-AzureAD
$sp=Get-AzureADServicePrincipal -ObjectId <the sp object id your copy>
$role=Get-AzureADDirectoryRole | Where-Object{$_.DisplayName -eq "Directory Readers"}
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $sp.ObjectId
Test
a. create a new runbook
$servicePrincipalConnection=Get-AutomationConnection -Name 'AzureRunAsConnection'
$connectState = Connect-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
if ($connectState) {
"Connected."
} else {
"Doesn't seem to be connected."
}
Get-AzADGroup
Go to Azure portal --> Azure AD --> roles and Administrator-->Directory Readers role --> assign this role to the runbook account name

Set-AzureRmContext error when executed within an Azure Automation Runbook

Update:
Seems like someone else had the same issue and reported it.
I am facing an issue with a simple PowerShell script when invoking it from an Azure Automation Runbook. The same piece of code works flawless when running it locally.
I have added a Service Principal in an Azure Active Directory (hosted in Azure German Cloud) with password credential and grant it contributor access to a subscription (also hosted in Azure German Cloud).
The Azure Automation service is hosted in North Europe since it's currently not available in the Azure German Cloud.
All I try to do is to login to my subscription with the above mentioned principal using the Add-AzureRmAccount cmdlet. After that I try to set the current context using the Set-AzureRmContext and getting the following error message:
Set-AzureRmContext : Please provide a valid tenant or a valid subscription.
At line:26 char:1
+ Set-AzureRmContext -TenantId $TenantId -Su ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Set-AzureRmContext], ArgumentException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.SetAzureRMContextCommand
Here is the script I try to run (left the configuration blank):
$TenantId = ""
$ApplicationId = ""
$ClientSecret = ""
$SubscriptionId = ""
$secpasswd = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($ApplicationId , $secpasswd)
Add-AzureRmAccount -ServicePrincipal -Environment 'AzureGermanCloud' -Credential $mycreds -TenantId $TenantId
Set-AzureRmContext -TenantId $TenantId -SubscriptionId $SubscriptionId
I also tried to use Login-AzureRmAccount without success. Also I am able to use the Get-AzureRmResourceGroup cmdlet to retrieve the resource groups so the login seems to work.
All Azure modules are updated to the latest version.
TLTR:
My main goal is to start a SQL export job using the New-AzureRmSqlDatabaseExport from the runnbook but it seems like the above mentioned error causes the cmdlet to fail with the following message:
New-AzureRmSqlDatabaseExport : Your Azure credentials have not been set up or have expired, please run
Login-AzureRMAccount to set up your Azure credentials.
At line:77 char:18
+ ... rtRequest = New-AzureRmSqlDatabaseExport -ResourceGroupName $Resource
I had the same issue a few weeks ago and what worked was to first login to Azure account (which I think you already did) using:
Login-AzureRmAccount
Then get the subscription ID from Azure and use select the subscription using the ID instead of the name as follows:
Select-AzureRmSubscription -SubscriptionId {insert-subscription-id}
Below is the code that worked for me (regular dc regions). If it doesn't work, go to the Automation Account >> Modules >> Update Azure Modules.
$ClientSecret = ""
$ApplicationId = ""
$SubscriptionId = ""
#New PSCredential Object
$secpasswd = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($ApplicationId , $secpasswd)
#Login to subscription
Login-AzureRmAccount -Credential $mycreds -SubscriptionId $SubscriptionId
#Export Database
New-AzureRmSqlDatabaseExport -ResourceGroupName "<RG>" -ServerName "<SQLSERVERNAME>" -DatabaseName "<DATABASENAME>" -StorageKeyType "StorageAccessKey" -StorageKey "<STRKEY>" -StorageUri "<URITOFILE>" -AdministratorLogin "<DBLOGIN>" -AdministratorLoginPassword "<DBPASS>"
Update
Maybe running with a Run As Account can be a workaround for the issue. Create one by navigating to the Azure Automation Account >> Account Settings >> Run As Accounts. Here's an example code.
# Authenticate to Azure with service principal and certificate, and set subscription
$connectionAssetName = "AzureRunAsConnection"
$conn = Get-AutomationConnection -Name $ConnectionAssetName
Add-AzureRmAccount -ServicePrincipal -Tenant $conn.TenantID -ApplicationId $conn.ApplicationId -CertificateThumbprint $conn.CertificateThumbprint -ErrorAction Stop | Write-Verbose
Set-AzureRmContext -SubscriptionId $conn.SubscriptionId -ErrorAction Stop | Write-Verbose
It looks like this is a known issue and I wasn't able to find a fix for that. But there are two workarounds:
Using a Hybrid Runnbook Worker (mentioned by Walter - MSFT)
Using a RunAsAccount with certificate credentials (mentioned by Bruno Faria)
It is important to specify the -Environment parameter. Otherwise I got the following exception:
Login-AzureRmAccount : AADSTS90038: Confidential Client is not
supported in Cross Cloud request.
Here is the code I am using to login to AzureGermanCloud (MCD) from an Azure Runbook hosted in NorthEurope:
$connectionAssetName = "AzureRunAsConnection"
$conn = Get-AutomationConnection -Name $ConnectionAssetName
Login-AzureRmAccount `
-ServicePrincipal `
-CertificateThumbprint $conn.CertificateThumbprint `
-ApplicationId $conn.ApplicationId `
-TenantId $conn.TenantID `
-Environment AzureGermanCloud
When you login your Azure account, you could use specified subscription id. You could try following script.
$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 -Environment 'AzureGermanCloud'

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.

VSTS Build and PowerShell and AzureAD Authentication

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