I'm creating an Azure AD application using AzureAD module to call Microsoft Graph API. I can successfully generate the access token. But, when I try to call the API I have an error "message": "Invalid scope claims/roles.".
When I click on "Grant Permissions" button in my created application in Azure Portal and retry the call to API, the call is working.
I don't find anywhere how to do this "Grant Permissions" actions with Powershell. Is there a way to do that ?
Thanks
Damien
There is an easy way to do this (as admin), it requires you have the AzureAD and AzureRM modules installed for Powershell and is not supported by Microsoft.
Original post / reference to my blog is here: http://www.lieben.nu/liebensraum/2018/04/how-to-grant-oauth2-permissions-to-an-azure-ad-application-using-powershell-unattended-silently/
The specific code sample that should help you accomplish this:
Function Grant-OAuth2PermissionsToApp{
Param(
[Parameter(Mandatory=$true)]$Username, #global administrator username
[Parameter(Mandatory=$true)]$Password, #global administrator password
[Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to
)
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
$res = login-azurermaccount -Credential $mycreds
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = #($context.TokenCache.ReadItems() | where {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
$body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
$apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
$header = #{
'Authorization' = 'Bearer ' + $apiToken.access_token
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()}
$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $header -Method POST -ErrorAction Stop
}
I came across the same error 'Refresh token is malformed'. When reading out the refreshtoken the token was twice in the string. Resolved it by adding the line
$refreshtoken = $refreshtoken.Split("`n")[0]
If I am not wrong, then it is using "Admin Consent". In that case, you should be using &prompt=admin_consent in the auth request directly.
If your application requests an app-only permission and a user tries to sign in to the application, an error message is displayed saying the user isn’t able to consent.
Whether or not a permission requires admin consent is determined by the developer that published the resource, and can be found in the documentation for the resource.
Link: Multi-tenant App pattern
List of Available permissions for the Azure AD Graph API and Microsoft Graph API are
Graph API Permission Scopes
Consent Framework
Hope it helps.
This answer builds on top of Jos's answer.
Active Directory Authentication library doesn't make the refresh tokens publicly available anymore. More information can be found in github/azure-powershell/7525.
The modified snippet below worked for me
Connect-AzAccount
$context = Get-AzContext
$tenantId = $context.Tenant.TenantId
Connect-AzureAD -TenantId $tenantId -AccountId $context.Account.Id
$appId = 'Your Application ID'
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $tenantId, $null, "Never", $null, "74658136-14ec-4630-ad9b-26e160ff0fc6")
$headers = #{
'Authorization' = 'Bearer ' + $token.AccessToken
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()}
$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$appId/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $headers -Method POST -ErrorAction Stop
Related
Currently we use an approach to reach DevOps and trigger "release pipelines" from a specific VM1 by utilizing user's DevOps PAT. We run PowerShell commands below at VM1:
$userPatToken = "xxxdfdgklfdgofkglfg4565gfhgfhgfh4gf54h54545fhfghfdffg"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $userPatToken)))
$url = "https://vsrm.dev.azure.com/MyOrg/MyProject/_apis/release/releases?definitionId=7&$top=100&api-version=6.0"
Invoke-RestMethod -Method Get -Uri $url -ContentType "application/json" -Headers #{Authorization = ("Basic {0}" -f $base64AuthInfo) }
The user is AAD one, is there a way to use it's let say AAD credentials and authenticate to DevOps and do the same?
Or may there is a way to use VMs system managed (or any user managed) identity to authenticate into DevOps and trigger release pipelines? We do not want to be dependent of the user's PAT.
It should be written in PowerShell.
If you don't want to use the PAT, you can install Az powershell module, login with a user account which has the permission in your devops org via Connect-AzAccount.
Then, you can get the token via below command. Please note don't change the 499b84ac-1321-427f-aa17-267ca6975798 in the script, it is the well-known resource id of the DevOps REST API.
$token = (Get-AzAccessToken -ResourceUrl "499b84ac-1321-427f-aa17-267ca6975798").Token
Then, you can pass the token to your powershell script. You can find more details/sample script here.
Edit:
Add username&Password automation script sample:
Install-Module -Name Az -Confirm:$False -Force -AllowClobber
Import-Module Az
$username = "useremail"
$password = "password"
$SecurePassword = ConvertTo-SecureString "$password" -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username, $SecurePassword)
Connect-AzAccount -Credential $credentials -TenantId yourTenantID
$token = (Get-AzAccessToken -ResourceUrl "499b84ac-1321-427f-aa17-267ca6975798").Token
$URL = 'https://dev.azure.com/{orgname}/{Projectname}/_apis/pipelines/{pipelineID}/runs?api-version=6.0-preview.1'
$header = #{
'Authorization' = 'Bearer ' + $token
'Content-Type' = 'application/json'
}
$body = #"
{
"resources": {
"repositories": {
"self": {
"refName": "refs/heads/master"
}
}
}
}
"#
Invoke-RestMethod -Method Post -Uri $URL -Headers $header -Body $body
I need to acquire access token for accessing resources in Azure (https://management.azure.com endpoint) using REST API. Every article a have read, was counting with Appliction Id. In my case, the Azure tenant was just created (programatically) and I have to create some resources in it.
Only thing I have is tenant id, subscription id, user name and password of admin account. How can I authenticate using only information I have? How it works in PowerShell, that does not need to use an Application Id?
Based on my knowledge, it is impossible. As junnas said, even you use user/password authentication, client id is also required.
It is easy for you to create a service principal on Azure, you could check this link.
After the sp is created, you will get the client id, client secret. You also need give the sp Owner role on subscription, you could check this link.
Now, you could use the sp to call rest api in Power Shell, for example.
##get token
$TENANTID=""
$APPID=""
$PASSWORD=""
$result=Invoke-RestMethod -Uri https://login.microsoftonline.com/$TENANTID/oauth2/token?api-version=1.0 -Method Post -Body #{"grant_type" = "client_credentials"; "resource" = "https://management.core.windows.net/"; "client_id" = "$APPID"; "client_secret" = "$PASSWORD" }
$token=$result.access_token
##set subscriptionId and resource group name
$subscriptionId=""
$resourcegroupname="shui5"
$Headers=#{
'authorization'="Bearer $token"
'host'="management.azure.com"
'contentype'='application/json'
}
$body='{
"location": "northeurope",
"tags": {
"tagname1": "test-tag"
}
}'
Invoke-RestMethod -Uri "https://management.azure.com/subscriptions/$subscriptionId/resourcegroups/${resourcegroupname}?api-version=2015-01-01" -Headers $Headers -Method PUT -Body $body
You can use the Microsoft PowerShell Application ID to authenticate without having your own Application ID. This code snipet will give you the token:
Import-Module MSOnline # IMPORTANT! Loads type assembly Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext
$TENANTID = "" # Your Tenant ID
$clientId = "1b730954-1685-4b74-9bfd-dac224a7b894" # PowerShell Client Id
$MSMgmtURI = "https://management.core.windows.net"
$authority = "https://login.microsoftonline.com/$TENANTID"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$authResult = $authContext.AcquireToken($MSMgmtURI, $clientId, $redirectUri, "Always")
$token = $authResult.AccessToken
$headers = #{'Authorization' = "Bearer $token", 'host'="management.azure.com", 'Content-Type' = "application/json"}
This is good for getting an access token using the login dialog. I suspect that the Client ID for PowerShell could be used in the Oath2 call above.
I've been having a hell of a time trying to access the Microsoft Graph using PowerShell.
First I tried using the authorization flow and Invoke-WebRequest and Invoke-RestMethod neither of which I could get to work.
Then I found this blog that showed how to do it using PowerShell and a couple of the Azure modules. Below is the code I'm using (ripped right from that blog) but every time I get to the Invoke-RestMethod (in the do-while loop) instead of getting the result of the query I get a 403 Forbidden error:
Invoke-RestMethod : The remote server returned an error: (403) Forbidden.
Function GetAuthToken {
Param (
[Parameter()]
$TenantName
)
Import-Module Azure
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
$resourceAppIdURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/$TenantName"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$Credential = Get-Credential
$AADCredentialUser = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $credential.UserName, $credential.Password
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $AADCredentialUser)
Write-Output $authResult
}
Function GetAllObjectOfType {
param
(
[Parameter(Mandatory = $true)]
$Tenant,
[Parameter(Mandatory = $true)]
$Type,
[Parameter(Mandatory = $false)]
$BatchSize = 100,
[Parameter(Mandatory = $false)]
$Version = 'Beta'
)
#------Get the authorization token------#
$token = GetAuthToken -TenantName $tenant
#------Building Rest Api header with authorization token------#
$authHeader = #{
'Content-Type' = 'application/json'
'Authorization' = $token.CreateAuthorizationHeader()
}
#------Initial URI Construction------#
#$uritest = "https://graph.microsoft.com/v1.0/users/user#contoso.com/mailFolders/Inbox/childFolders"
$uritest = "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox"
#Join-Path -Path ''
$ObjCapture = #()
do {
$users = Invoke-RestMethod -Uri $uritest -Headers $authHeader -Method Get
$FoundUsers = ($Users.value).count
write-host "URI" $uri " | Found:" $FoundUsers
#------Batched URI Construction------#
$uri = $users.'#odata.nextlink'
$ObjCapture = $ObjCapture + $users.value
}until ($uri -eq $null)
$ObjCapture
}
I can run this same query (/v1.0/me/mailFolders/Inbox) from the Graph Explorer and it runs perfectly fine with no errors.
The GetAuthToken seems to be working as I do get a token back with an expiry, refresh token, etc and the $token.CreateAuthorizationHeader() also returns the correct Authorization = Bearer token
I've never done anything with the Microsoft Graph before so I'm sure there is something I'm doing wrong but I cannot for the life of me figure out what.
You cannot reuse the clientid from that blog post. You need to obtain your own clientid by registering your application. See Register your app with the Azure AD v2.0 endpoint for details on how to register your app.
This seems like it was answered in a follow-up blog post from the same source.
When an App is created, by default it has rights to access only the data of
the user that had signed in with the account though the “Sign in and read user
profile” delegated permissions. If you try to execute a script that uses this
AppID/ClientID to query Azure AD to get a list of all users in the directory,
you would receive an error stating (403) Forbidden because it didn’t have
adequate permissions to do that activity.
Putting this here in case any other wandering devs come across the post like I did. Here's the powershell function I'm using to get the access token from our tenant. ClientId and Secret are created in the Azure App Registrations, like others have mentioned.
$clientId = $args[0]
$secret = $args[1]
$redeemURI = "https://login.microsoftonline.com/{tenantGuid}/oauth2/v2.0/token"
$body = "client_id=$clientId&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=$secret&grant_type=client_credentials"
$response = Invoke-RestMethod -Method Post -Uri $redeemURI -Body $body -ContentType "application/x-www-form-urlencoded"
return $response.access_token
Usage:
$token = ./GetAccessToken.ps1 "{clientId}" "{secret}"
I think it needs AD Azure Premium 2 licences
enter link description here
I use the following PowerShell code to access MS Graph API.
Import-Module MSOnline
$User = "UserName"
$Password = "Password"
$TenantName = "tenantname.onmicrosoft.com"
$clientId = "clientId"
$authority = "https://login.microsoftonline.com/$TenantName"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $AdminUser, $Password
$resourceAppIdURI = "https://graph.microsoft.com"
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId,$AADCredential)
$authHeader = #{
'Content-Type'='application\json'
'Authorization'=$authResult.CreateAuthorizationHeader()
}
$uri = "https://graph.microsoft.com/beta/$TenantName/reports/getEmailActivityUserDetail(period='D7')"
Invoke-RestMethod -Uri $uri -Method Get -Headers $authHeader
It works fine when the user and tenant agree. What I would like to do is access our client tenants using an admin with delegated permissions. I've set the app to have Pre-consent permissions, which according to https://developer.microsoft.com/en-us/graph/docs/concepts/auth_cloudsolutionprovider should also allow the behaviour I am after. However, when I run the code with a partner admin credentials and client tenantname, I receive a 400 bad request error. What step am I missing here?
Per the documentation (see the important note at the beginning of the topic), for CSP pre-consent, only Directory and Intune resources in Microsoft Graph supports the partner model. Over time, more resources will support the partner/CSP model. Please create a request for this on UserVoice if this is important to you.
Hope this helps,
Using the latest Azure Powershell SDK, but still can't seem to create Custom SSL Domains for CDNs in Azure via API Management. We have 100s of subdomains to create and need to be able to script the creation of this task for future extensibility.
Does anyone know how to toggle this flag via the REST API since the SDK has no support? We are using the New-AzureRmCdnCustomDomain commandlet.
Update: The AzureRM 6.13.0-module and the new Az-modules (including Az.Cdn) now supports this using a cmdlet. See Enable-AzureCdnCustomDomain (AzureRM.Cdn) or Enable-AzCdnCustomDomain (Az.Cdn)
The REST API for enabling Custom Domain HTTPS is documented at learn.microsoft.com
Enable Custom Https
Enable https delivery of the custom domain.
POST /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cdn/profiles/{profileName}/endpoints/{endpointName}/customDomains/{customDomainName}/enableCustomHttps?api-version=2017-10-12
Before you can use the Azure REST API you need to get an access token:
Generating access token using PowerShell:
$Token = Invoke-RestMethod -Uri https://login.microsoftonline.com/<TenantID>/oauth2/token?api-version=1.0 -Method Post -Body #{
"grant_type" = "client_credentials"
"resource" = "https://management.core.windows.net/"
"client_id" = "<application id>"
"client_secret" = "<password you selected for authentication>"
}
The response contains an access token, information about how long that
token is valid, and information about what resource you can use that
token for. The access token you received in the previous HTTP call
must be passed in for all request to the Resource Manager API. You
pass it as a header value named "Authorization" with the value "Bearer
YOUR_ACCESS_TOKEN". Notice the space between "Bearer" and your access
token.
Client ID is retrived by creating an app registration in Azure AD and the clientkey is generated in the Keys-section of the created app registration. This can be combined into a solution like this:
$subscriptionId = "..."
$resourceGroupName = "..."
$profileName = "..."
$endpointName = "..."
$customDomainName = ".."
$Token = Invoke-RestMethod -Uri https://login.microsoftonline.com/<TenantID>/oauth2/token?api-version=1.0 -Method Post -Body #{
"grant_type" = "client_credentials"
"resource" = "https://management.core.windows.net/"
"client_id" = "<application id>"
"client_secret" = "<password you selected for authentication>"
}
$header = #{
"Authorization"= "Bearer $($Token.access_token)"
}
Invoke-RestMethod -Method Post -Headers $header -Uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Cdn/profiles/$profileName/endpoints/$endpointName/customDomains/$customDomainName/enableCustomHttps?api-version=2016-10-02"
If you don't need to automate the script, you can login manually using GUI (no need for app-registration) using this modified sample (based on Source). It requires AzureRM-module, which can be installed using Install-Module AzureRM:
Function Login-AzureRESTApi {
Import-Module AzureRM.Profile
# Load ADAL Azure AD Authentication Library Assemblies
$modulepath = Split-Path (Get-Module -Name AzureRM.Profile).Path
$adal = "$modulepath\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = "$modulepath\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
$null = [System.Reflection.Assembly]::LoadFrom($adal)
$null = [System.Reflection.Assembly]::LoadFrom($adalforms)
# Login to Azure
$Env = Login-AzureRmAccount
# Select Subscription
$Subscription = (Get-AzureRmSubscription | Out-GridView -Title "Choose a subscription ..." -PassThru)
$adTenant = $Subscription.TenantId
$global:SubscriptionID = $Subscription.SubscriptionId
# Client ID for Azure PowerShell
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
# Set redirect URI for Azure PowerShell
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
# Set Resource URI to Azure Service Management API | #marckean
$resourceAppIdURIASM = "https://management.core.windows.net/"
$resourceAppIdURIARM = "https://management.azure.com/"
# Set Authority to Azure AD Tenant
$authority = "https://login.windows.net/$adTenant"
# Create Authentication Context tied to Azure AD Tenant
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
# Acquire token
$global:authResultASM = $authContext.AcquireToken($resourceAppIdURIASM, $clientId, $redirectUri, "Auto")
$global:authResultARM = $authContext.AcquireToken($resourceAppIdURIARM, $clientId, $redirectUri, "Auto")
}
$resourceGroupName = "..."
$profileName = "..."
$endpointName = "..."
$customDomainName = ".."
Login-AzureRESTApi
#Reuse selected subscription from login
$Subscription = $global:subscriptionId
$header = #{
"Authorization"= $global:authResultARM.CreateAuthorizationHeader()
}
Invoke-RestMethod -Method Post -Headers $header -Uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Cdn/profiles/$profileName/endpoints/$endpointName/customDomains/$customDomainName/enableCustomHttps?api-version=2017-10-12"