MS Graph API access client tenant with partner credentials - powershell

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,

Related

Login to power bi service online (silently i.e. without the popup) using powershell

I would like to login to Power BI Online service and remove rows from a dataset using the REST API. I have the rest of the code going fine but the login seems to not work. This is what I tried. Can someone help me please? Thank you!
$pbiUsername = "abc.xyz#xxx.com"
$pbiPassword = "Password"
$clientId = "a81b2cc1-4c97-2323-bal4-eeb21c4c6e46"
$body = #{"resource" = "https://analysis.windows.net/powerbi/api"
"client_id" = $clientId;
"grant_type" = "password";
"username" = $pbiUsername;
"password" = $pbiPassword;
"scope" = "openid"
}
$authUrl = "https://login.windows.net/common/oauth2/token/"
$authResponse = Invoke-RestMethod -Uri $authUrl –Method POST -Body $body
$headers = #{
"Content-Type" = "application/json";
"Authorization" = $authResponse.token_type + " " +
$authResponse.access_token
}
$restURL = "https://api.powerbi.com/v1.0/myorg/groups"
$restResponse = Invoke-RestMethod -Uri $restURL –Method GET -Headers $headers
"Login doesn't seems to work" doesn't give us enough information to hint you what is the problem.
I will recommend you to use the official Microsoft Power BI Cmdlets to do tasks like this. It has big advantage - you don't need to register an application to use it. Here is how your code would look like in this case:
Import-Module MicrosoftPowerBIMgmt
Import-Module MicrosoftPowerBIMgmt.Profile
$password = "Password" | ConvertTo-SecureString -asPlainText -Force
$username = "abc.xyz#xxx.com"
$credential = New-Object System.Management.Automation.PSCredential($username, $password)
Connect-PowerBIServiceAccount -Credential $credential
Invoke-PowerBIRestMethod -Url 'groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/datasets/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/tables/xxxxxx/rows' -Method Delete
Disconnect-PowerBIServiceAccount
Since PowerBI now has enabled the Service Principal, you don't need to worry about the username and password and more importantly, you DON'T NEED PRO Licenses
using the Service Principal you can now able to log in without popup as well as username, password
$applicationId = "xxxxxxxx";
$securePassword = "xxxxxx" | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $applicationId, $securePassword
Connect-PowerBIServiceAccount -ServicePrincipal -Credential $credential -TenantId "xxxx"
The error is not handled with try catch - you still get the same error with this:
try {Connect-PowerBIServiceAccount} catch {"Boo"}
I follow the steps to fix this issue.
RESOLUTION
To resolve this issue, we need to install the correct module. It may not be the latest build, but the builds have to match in order for things to work for us. In my scenario, I installed the MicrosoftPowerBIMgmt.Workspaces 1.0.830 so that it matched the MicrosoftPowerBIMgmt.Profile.
I went to the PowerShell Gallery to get the Install-Module cmd.
https://www.powershellgallery.com/packages/MicrosoftPowerBIMgmt/1.0.326
From an Administrative Command-Prompt, type Install-Module -Name MicrosoftPowerBIMgmt.Workspaces -RequiredVersion 1.0.830
NOTE: Remember, for my lab scenario, I used the MicrosoftPowerBIMgmt.Workspaces Module. You will need to install the module that you are focused.
Once installed, close all PowerShell Windows or open a brand new PowerShell window and then type Get-Module. Now the installed modules should match.
NOTE: You have to start a new PowerShell session. If you don’t the error may not go away.
https://dastrongman.wordpress.com/2020/01/11/pbiwiki-login-with-the-power-bi-service-account/

How to authenticate to Azure REST API using username and password (no App Id)

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.

Accessing Microsoft Graph With PowerShell

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

Powershell - Do "Grant Permissions" action on Azure AD Application with Powershell

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

Azure CDN - Custom Domain SSL via Resource Management API

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"