Getting "remote server recieving 401 unauthorized when invoke-restmethod token request in Azure runbooks

AdminAS 141 Reputation points
2021-10-26T22:15:00.92+00:00

I need to use unattended login for the Microsoft Teams module. To do this I need the Graph and Teams resource tokens to pass to the connection. I have created a http call body for these requests and tested on my machine. They work. When I take the code and place it into the runbook environment i get the "401 Unauthorized" error.

I have given all the proper API permissions to the Service Principal i am using. And gone off this tutorial for the most part, missing the runbooks. teams-powershell-token-auth

Here is the code, minimizing the hardcoding as you would assume.

$Application = Get-AutomationCertificate -Name 'Onboarding-ServicePrincipal.PFX'
$Application
$connectionID = Get-AutomationConnection -Name 'Onboarding-ServicePrincipal'

Connect-ExchangeOnline -AppId $connectionID.ApplicationId -CertificateThumbprint $Application.Thumbprint -Organization "MyCompany.onmicrosoft.com" -verbose

$servicePrincipal = Get-AutomationPSCredential -Name 'Onboarding-ServicePrincipal'
$tenantName = "MyCompany.onmicrosoft.com"
$clientSecret = $servicePrincipal.password

write-output "Connection Details:"
$connectionID.ApplicationId
$servicePrincipal.password
write-output "########################"
$graphResource = "https://graph.microsoft.com/"
$graphTokenBody = @{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $connectionID.ApplicationId
Client_Secret = $clientSecret
}
$graphUri = "https://login.microsoftonline.com/"+$tenantName+"/oauth2/v2.0/token"
$graphUri
$graphTokenResponse = Invoke-RestMethod -Uri $graphUri -Method POST -body $graphTokenBody
write-output "Here is Graph....."
$graphTokenResponse

$teamsResource = "https://api.interfacesrecords.teams.microsoft.com"
$teamsTokenBody = @{
Grant_Type = "client_credentials"
Scope = "48ac35b8-9aa8-4d74-927d-1f4a14a0b239/.default"
Client_Id = $connectionID.ApplicationId
Client_Secret = $clientSecret
}
$teamsUri = "https://login.microsoftonline.com/"+$tenantName+"/oauth2/v2.0/token"
$teamsTokenResponse = Invoke-RestMethod -Uri $teamsUri -Method POST -Body $teamsTokenBody
write-output "Here is Teams...."
$teamsTokenResponse

$servicePrincipal.ClientID

Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
10,716 questions
Azure Automation
Azure Automation
An Azure service that is used to automate, configure, and install updates across hybrid environments.
1,132 questions
Microsoft Partner Center API
Microsoft Partner Center API
Microsoft Partner Center: A Microsoft website for partners that provides access to product support, a partner community, and other partner services.API: A software intermediary that allows two applications to interact with each other.
317 questions
0 comments No comments
{count} votes

Accepted answer
  1. AdminAS 141 Reputation points
    2021-10-27T15:44:18.577+00:00

    ANSWER:
    So if anyone else stumbles on my answering my own problems here ya go.

    It was exactly what i said, the system was sending over the literal "system.securestring" which is obviously not the password. So looking over this answer graciously given by @mklement0 how-do-you-decrypt-securestring-in-powershell I took the psobject password in a temporary variable and then pass the plaintext password decrypted in another variable to the POST. Here is the blurp:

    $secret = $servicePrincipal.password
    $clientSecret = [Net.NetworkCredential]::new('',$secret).Password

    .............

    $graphTokenBody = @{
    grant_Type = "client_credentials"
    tenant = $tenant
    scope = "https://graph.microsoft.com/.default"
    client_id = $connectionID.ApplicationId
    client_secret = $clientSecret
    }

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. AdminAS 141 Reputation points
    2021-10-27T15:28:20.537+00:00

    EDIT:

    This is what I found, in that code i pull in the Client secret from the account.password attribute. I have seen this translate in other things, but it does not in this case. I hardcoded my client secret into the code and it worked. NOW I ask if there is a way to disguise the client secret within the code (aka use the $client.Password) but input the "conversion" of the password as Plaintext into the body of the Call??

    0 comments No comments