Unable to reset the password for a disabled account?!

So apparently, when using the recently introduced /resetPassword endpoint from the Authentication methods Graph API branch, we cannot reset the password for any user whose account is currently disabled (as in BlockCredential is set to True/AccountEnabled is set to False). No idea whether this is some inherent Graph API limitation or someone decided it’s how things should work. In any case, this is what you get currently:

$uri = "https://graph.microsoft.com/beta/users/user@domain.com/authentication/passwordMethods/28c10230-6103-485e-b985-444c60001490/resetPassword"
Invoke-WebRequest -Headers $authHeader -Uri $uri -Method Post

Invoke-WebRequest : The remote server returned an error: (400) Bad Request.
At line:2 char:13
+ Invoke-WebRequest -Headers $authHeader -Uri $uri -Method ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Note that I didn’t specify a password in the POST request, but that’s perfectly acceptable for a cloud-only account and will result in generating a random password. And you will not get better results by actually providing a password value either. The error message itself is beyond helpful, so I resorted to using the good old StreamReader method to get the actual HTTP error response:

$uri = "https://graph.microsoft.com/beta/users/user@domain.com/authentication/passwordMethods/28c10230-6103-485e-b985-444c60001490/resetPassword"
try { Invoke-WebRequest -Headers $authHeader -Uri $uri -Method Post -ErrorAction Stop -Body ($body | ConvertTo-Json -Depth 6) }
catch [System.Net.WebException] {
if ($_.Exception.Response -eq $null) { throw }

#Get the full error response
$streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
$streamReader.BaseStream.Position = 0
$errResp = $streamReader.ReadToEnd() | ConvertFrom-Json
$streamReader.Close()

if ($errResp.error.code -match "ResourceNotFound|Request_ResourceNotFound") { Write-Verbose "Resource $uri not found, skipping..."; return } #404, continue
elseif ($errResp.error.code -eq "BadRequest") { $errResp | return } #400, we should terminate... but stupid Graph sometimes returns 400 instead of 404
elseif ($errResp.error.code -eq "Forbidden") { Write-Verbose "Insufficient permissions to run the Graph API call, aborting..."; throw } #403, terminate
elseif ($errResp.error.code -eq "InvalidAuthenticationToken") { Write-Verbose "Access token is invalid, exiting the script." ; throw }
else { $errResp ; throw }
}
catch { $_ ; return }

which in turn gives us a proper error message:

error
-----
@{code=badRequest; message={"error":{"code":"BadRequest","message":"User is blocked","innerError":{"request-id":"27fcaac5-bc4a-4b77-8152-02742a5dabf6","date":"2021-05-18T16:11:44.908418Z"}}}; innerError=}

Trying the same operation via the Azure AD blade will result in the following message:

BlockedAccountPasswordResetFortunately, we can work around this idiocy by using the good old Set-MsolUserPassword cmdlet or performing the reset via the Microsoft 365 Admin Center. For now.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.