Outlook rest-api from powershell returning oath request (401) - azure

I'm trying to create an auto notification app from outlook,
which I tried to use outlook rest API to do that.
for now I'm just making a simple code, but the result always returning error code 401
I've tried to register my apps into app registration on my Azure tenants. but have no luck nor I understand what to do next...
$uri = "https://outlook.office365.com/api/v2.0/me/sendmail"
$userName = << my username >>
$password = << my password >>
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName,$password
$body = "{
""Subject"":""rest API test"",
""Importance"":""High"",
""Body"":{
""ContentType"":""HTML"",
""Content"":""test test 1 2 3""
},
""ToRecipients"":[{
""Address"":""<< my recipients >>""
}]
}"
Invoke-RestMethod -Uri $uri -Method Post -Credential $credb -ContentType "application/json" -Body $body
the result from api said :
{"error":{"code":"OAuthMissingForThisApiVersion","message":"Authentication for this API version requires OAuth."}}
which is why I tried to register my app to azure app registration to get my token.
do anyone knows how to solve this problem?
or even some tutorial to show me how to set an app registration until I can get the token to my powershell app.
regards,

https://outlook.office.com/api/v1.0 doesn't support Basic auth as OAuth is the recommended auth mechanism, but you can continue to use https://outlook.office365.com/api/v1.0 if you need to keep using Basic auth.
Try something like below, it should work
$uri = "https://outlook.office365.com/api/v1.0/me/sendmail"
$UserName = "mv.v#my.domain"
$Password = cat C:\MydomainCreds.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username,$password
$body = "{
""Message"":{
""Subject"": ""This is a send test"",
""Importance"": ""High"",
""Body"": {
""ContentType"": ""HTML"",
""Content"": ""How about this for a surprise!""
},
""ToRecipients"": [
{
""EmailAddress"":{
""Address"": ""mytestmailbox#anywhere.com""
}
}
]
}}"
Invoke-RestMethod -Uri $uri -Method Post -Credential $cred -ContentType "application/json" -Body $Body
Hope it helps.

Related

Unable to create Azure B2C User Flow via powershell with b2cIdentityUserFlow

I am trying to create a user flow with PowerShell, but I receiveThe remote server returned an error: (403) Forbidden.. I was reading the documentation from Microsoft but with no success.
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\azdeploy\flows\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
JSON Content(Default From Microsoft Docs):
{
"id": "Customer",
"userFlowType": "signUpOrSignIn",
"userFlowTypeVersion": 3
}
Connect-AzAccount is made with a user who is Global Administrator, also tried with Lifecycle Workflows Administrator permissions. I don't know what to do, trying the old API but it is deprecated. I need to create a few User Flows with а few Application Claims. How can I achieve this?
Thanks!
I tried to reproduce the same in my environment and got below results:
I created one json file with same parameters as you like below:
I have one user named Sritest having Global Administrator role like below:
When I ran the same code as you by signing in with above user, I got same error as below:
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
Response:
You need to have IdentityUserFlow.ReadWrite.All permission to create userflow.
To resolve the error, I registered one Azure AD application and added that API permission like below:
Make sure to grant admin consent after adding API permissions in application. Now, I created one client secret and added all these details in getting access token by modifying PowerShell code.
When I ran below modified code, userflow created successfully as below:
Connect-AzureAD -TenantId "c6d99123-0cf9-4b64-bde3-xxxxxxxxx"
$graphtokenBody = #{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = "appID"
client_secret = "secret"
}
$graphToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/c6d99123-0cf9-4b64-bde3-xxxxxxxxx/oauth2/v2.0/token" -Method POST -Body $graphtokenBody
$token = $graphToken.access_token
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($token)";
} `
-Body $signinFlowContent
Response:
To confirm that, I checked the same in Portal where B2C_1_Customer userflow is present like below:

Using token from Connect-AZAccount on Intune Graph calls

So, after years consuming valuable information from StackOverflow this is my first question!
I used part of a code I found when researching as I start to move away from App Secrets in Graph API and prepare to use Managed Identities.
It successfully authenticate and generate the token when running locally(haven't tested anything on Azure Automation yet as this would be the PoC) but it will not work for everything in Graph, it works when querying AD Users and Groups but not on anything else, it seems it is not getting all the scopes the accounts has access to although the account is global admin.
This part will check if you are using Managed Identity or prompt for login
#Requires -Modules #{ ModuleName="Az.Accounts"; ModuleVersion="2.7.0" } , #{ ModuleName="Az.Resources"; ModuleVersion="5.1.0" }
Param(
[Switch]$nonInteractive
)
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
$res = [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
if(!$Global:AZConnection){
try{
if($nonInteractive){
Write-Output "Logging in with MI"
$Null = Connect-AzAccount -Identity -ErrorAction Stop
Write-Output "Logged in as MI"
}else{
Write-Output "Logging in to Azure AD"
$Global:AZConnection = Connect-AzAccount -Force -ErrorAction Stop
Write-Output "Logged in to Azure AD with $($Global:AZConnection.Context.Account.id)"
}
}catch{
Throw $_
}
}
After this it will get the context and authenticate against graph.microsoft.com & graph.windows.net to get both tokens.
It will also prepare the headers to be used on WebRequests
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$graph = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Always, $null, "https://graph.microsoft.com"))
$graphToken = $graph.AccessToken
$AAD = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.windows.net"))
$aadToken = $AAD.AccessToken
$HeadersGraph = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $graphToken"
}
$HeadersAAD = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $aadToken"
}
Trying to use the token to query user or group information will work
$UserData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
$GroupData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
But if I try to query any Intune or SharePoint URI it will give me:
Invoke-RestMethod : The remote server returned an error: (403) Forbidden.
$deviceData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices" -Method GET -Headers $HeadersGraph
$SharepointData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/sites" -Method GET -Headers $HeadersGraph
I can use these Tokens to authenticate on other modules but I was trying to avoid it and keep everything on Graph web requests
#This also works
$Intune = Connect-MgGraph -AccessToken $graphToken
$AzureAD = Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id
Does anyone know of any resource principal that I can authenticate that would give me a valid token to make calls to SharePoint and Intune?
I assume that you're aware that this is a permission issue and not a token issue.
I have no solution for your problem. I just want to let you know, that nowadays there is an easier way to get Graph Access tokens (since you are already using the Az.Accounts module).
https://learn.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken

How to get the access token to Azure API Management programmatically?

I'm trying to implement Azure Active Directory in my API Management instance using the Protect an API by using OAuth 2.0 with Azure Active Directory and API Management doc. The doc suggests that in order to get the access token I need to use the Developer Portal.
My problem is: An external application is going to communicate with API Management. Is there a way to omit the Developer Portal and get the access token programmatically?
It's a pain but thanks to Jos Lieben I am able to do it with this Powershell function
It's specifically for granting API access on behalf of the Org, but as you can see you can extract the commands to get and use the API token.
Original Author Link: https://www.lieben.nu/liebensraum/2018/04/how-to-grant-oauth2-permissions-to-an-azure-ad-application-using-powershell-unattended-silently/
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()
}
$script:url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $header -Method POST -ErrorAction Stop
}

List Cloud Service Classic use PowerShell and Azure Rest API

I Have a problem. Could you please help me view list Cloud Service Classic use PowerShell and Azure Rest API. When I used script for Web APP I show list Web APP, but when I used scrip for Cloud Service Classic I show error.
# Variables
$TenantId = "" # Enter Tenant Id.
$ClientId = "" # Enter Client Id.
$ClientSecret = "" # Enter Client Secret.
$Resource = "https://management.core.windows.net/"
$SubscriptionId = "" # Enter Subscription Id.
$RequestAccessTokenUri = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$body = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$Resource"
$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType 'application/x-www-form-urlencoded'
Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token
# Get Azure Resource Groups
$ResourceGroupApiUri = "https://management.core.windows.net/$SubscriptionId/services/hostedservices"
$Headers = #{}
$Headers.Add("Authorization","$($Token.token_type) "+ " " + "$($Token.access_token)")
$ResourceGroups = Invoke-RestMethod -Method Get -Uri $ResourceGroupApiUri -Headers $Headers
Write-Host "Print Resource groups" -ForegroundColor Green
Write-Output $ResourceGroups
Invoke-RestMethod : ForbiddenErrorThe server failed to authenticate the request. Verify that the certificate is valid and
is associated with this subscription.
Actually, there is a built-in ASM PowerShell to list the cloud services associated with the current subscription.
Get-AzureService
Reference - https://learn.microsoft.com/en-us/powershell/module/servicemanagement/azure/get-azureservice?view=azuresmps-4.0.0
Besides, if you insist on calling the ASM rest api with powershell, you could refer to this article, the sample calls the Get Deployment api, just change it to List Cloud Services.
#Request Headers required to invoke the GET DEPLOYMENT REST API
$method
=
“GET”
$headerDate
= ‘2009-10-01’
$headers
= #{“x-ms-version”=“$headerDate“}
#Retrieving the subscription ID
$subID
= (Get-AzureSubscription
-Current).SubscriptionId
$URI
=
https://management.core.windows.net/$subID/services/hostedservices/kaushalz/deployments/4f006bb7d2874dd4895f77a97b7938d0
#Retrieving the certificate from Local Store
$cert
= (Get-ChildItem
Cert:\CurrentUser\My
|
?{$_.Thumbprint -eq
“B4D460D985F1D07A6B9F8BFD67E36BC53A4490FC”}).GetRawCertData()
#converting the raw cert data to BASE64
body
=
“<Binary>—–BEGIN CERTIFICATE—–`n$([convert]::ToBase64String($cert))`n—–END CERTIFICATE—–</Binary>”
#Retrieving the certificate ThumbPrint
$mgmtCertThumb
= (Get-AzureSubscription
-Current).Certificate.Thumbprint
#Passing all the above parameters to Invoke-RestMethod cmdlet
Invoke-RestMethod
-Uri
$URI
-Method
$method
-Headers
$headers
-CertificateThumbprint
” B4D460D985F1D07A6B9F8BFD67E36BC53A4490FC”
-ContentType
$ContentType

How to script External collaboration settings in Azure

Would like to be able to control two settings in Azure Active Directory user settings:
External collaboration settings > “Members can invite”
External collaboration settings > “Guests can invite”
As we already know, you can control just about everything in Azure with powershell, except these two things.
The internet, azure docs, and other resources, known to me, have been utilized to no real results.
Code: this is what we are looking to start.
Would like to say "Get-AzAADExternalCollaborationsSettings" then use the results to say "Set-AzAADExternalCollaborationsSettings".
AzAADUserSettings Picture
AFAIK, there is no built-in powershell command for the external collaboration settings, the two settings call the azure undisclosed api https://main.iam.ad.ext.azure.com/api/xxxx, the workaround I can just find is to invoke the api with powershell, here is a sample for you to refer, it calls a different api, but the logic should be similar. You can catch the requests of the two settings via fiddler and follow the sample to have a try.
Sample:http://www.lieben.nu/liebensraum/2018/04/how-to-grant-oauth2-permissions-to-an-azure-ad-application-using-powershell-unattended-silently/
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-Object {$_.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
}
Through monitoring the webbrowser network reuest i was able to get the correct API URL https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties
This should be a Powershell command. Hopfully Microsoft does't break this API.
Here are some snippets of what i am using:
# Get API Token
$mycreds = Get-Credential
if($tenantId){
$res = login-azurermaccount -Credential $mycreds -TenantId $tenantId.ToLower()
}else{
$res = login-azurermaccount -Credential $mycreds
}
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = $context.TokenCache.ReadItems().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'
# Get current settings
$header = #{
'X-Requested-With'= 'XMLHttpRequest'
"Origin"="https://portal.azure.com"
'Authorization' = 'Bearer ' + $apiToken.access_token
'x-ms-client-request-id'= [guid]::NewGuid()
}
$url = "https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties"
Invoke-WebRequest -Uri $url -Headers $header -ContentType "application/json" -ErrorAction Stop
# Change settings
$settings = #{
allowInvitations = $true
limitedAccessCanAddExternalUsers = $false
restrictDirectoryAccess = $true
usersCanAddExternalUsers = $false
}
$body = $settings | ConvertTo-Json
$header = #{
'X-Requested-With'= 'XMLHttpRequest'
"Origin"="https://portal.azure.com"
'Authorization' = 'Bearer ' + $apiToken.access_token
'x-ms-client-request-id'= [guid]::NewGuid()
}
$url = "https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties"
Invoke-WebRequest -Uri $url -Method "PUT" -Headers $header -ContentType "application/json" -Body $body
Thnx for pointing me in the right direction Joy.

Resources