As part of something I was trying today I wanted to find out via Graph API whether a particular user has an O365 mailbox or not. There’s probably better ways to do this but I couldn’t find any and finally came up with the following.
The Graph API can return the mailboxSettings
for a user (see the docs). This is not returned by default, one needs to use the select
operator. I noticed that for a user who has a mailbox I get the following:
1 2 |
> Get-MgUser -UserId "ihazmailbox@domain.com" -Select mailboxSettings Get-MgUser_Get1: Access is denied. Check credentials and try again. |
Whereas for those with on-prem mailbox I get this:
1 2 |
> Get-MgUser -UserId "ihazonpremmailbox@domain.com" -Select mailboxSettings Get-MgUser_Get1: Resource could not be discovered. |
I thought I could put this into a try/ catch
block to maybe identify mailboxes that are on-prem or in the cloud:
1 2 3 4 5 6 7 |
try { Get-MgUser -UserId "abc@def.com" -Select mailboxSettings } catch { if ($_.Exception -match "denied") { "mailbox exists in O365" } elsif ($_.Exception -match "could not be discovered") { "mailbox exists on-prem/ or no mailbox" } } |
That doesn’t work unfortunately. The error isn’t captured in the try/ catch
block. (Update: this actually works, see below).
Next I thought let me try invoking the API directly.
After poking around I found that calling this URL https://graph.microsoft.com/v1.0/users/<upn>/mailboxSettings
returns output like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
HTTP/1.1 404 Not Found Date: Wed, 14 Jul 2021 12:52:27 GMT Cache-Control: private Transfer-Encoding: chunked Vary: Accept-Encoding Strict-Transport-Security: max-age=31536000 request-id: ed52732a-5bfe-4100-926b-58acc1691659 client-request-id: ed52732a-5bfe-4100-926b-58acc1691659 x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"UK South","Slice":"E","Ring":"3","ScaleUnit":"003","RoleInstance":"LO2PEPF0000003A"}} Content-Type: application/json Content-Encoding: gzip {"error":{"code":"ResourceNotFound","message":"Resource could not be discovered.","innerError":{"date":"2021-07-14T12:52:28","request-id":"ed52732a-5bfe-4100-926b-58acc1691659","client-request-id":"ed52732a-5bfe-4100-926b-58acc1691659"}}} |
Put that in a try/ catch
block and $_.Exception
has the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Response : StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Date: Wed, 14 Jul 2021 12:53:01 GMT Cache-Control: private Transfer-Encoding: chunked Vary: Accept-Encoding Strict-Transport-Security: max-age=31536000 request-id: 0f580d2d-467d-440f-875b-6db8efb3705c client-request-id: 0f580d2d-467d-440f-875b-6db8efb3705c x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"UK South","Slice":"E","Ring":"3","ScaleUnit":"003","RoleInstance":"LO2PEPF00000037"}} Content-Type: application/json Content-Encoding: gzip Content-Length: 238 } StatusCode : TargetSite : Void ThrowTerminatingError(System.Management.Automation.ErrorRecord) StackTrace : at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord) Message : Response status code does not indicate success: NotFound (Not Found). Data : {} InnerException : HelpLink : Source : System.Management.Automation HResult : -2146233088 |
Ok… digging in deeper I get that $_.Exception.Response.StatusCode
has NotFound
for an on-prem (or non-existent) mailbox and Forbidden
for one that exists (forbidden because my account doesn’t have rights to view mailboxes; if you were trying with a different account you might get something else… so beware). Thus we have the following:
1 2 3 4 5 6 7 8 9 10 11 12 |
try { Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/users/$upn/mailboxSettings" } catch { if ($_.Exception.Response.StatusCode -eq "Forbidden") { "mailbox exists in O365" } if ($_.Exception.Response.StatusCode -eq "NotFound") { if ((Get-MgUser -UserId $UPN).mail.length -eq 0) { "no mailbox" } else { "mailbox is on-prem" } } } |
As you can see I took things one step further and figure that if a user doesn’t have a mail attribute then the NotFound
message just means they don’t have a mailbox. (Could have just done this check first before the Graph query now that I think about it…)
I have no idea if this is the “correct” way to do this or I am just complicating matters, but here it is.
Updates:
- Determine if a Mailbox is On-Premises or in Office 365 with PowerShell – Uses the MSOnline cmdlets to check an attribute to give a better answer. I of course wanted to avoid this, but just adding this post as an FYI as this might be useful to others.
Update 2:
Every now and then you Google on something, forgetting you have posted about this in the past, and come across your own blog. Can’t beat that feeling! :)
My first attempt via Get-MgUser
works, just that I need to add -ErrorAction Stop
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
try { Get-MgUser -UserId $userObject.Id -Select mailboxSettings -EA Stop } catch { if ($_.Exception.Message -match "denied") { $userObject.UserPrincipalName + " mailbox exists in O365" } elseif ($_.Exception.Message -match " could not be discovered") { $userObject.UserPrincipalName + " mailbox exists on-prem/ or no mailbox" } elseif ($_.Exception.Message -match "inactive, soft-deleted, or is hosted on-premise" ) { $userObject.UserPrincipalName + " mailbox is inactive, soft-deleted, or is hosted on-premise" } else { $userObject.UserPrincipalName + " " + $_.Exception.Message } } |
It’s probably for the best that I didn’t go down this route though as it is not very good looking. I like what I stumbled upon eventually using the status code. Here’s a revised variant of that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
try { Get-MgUser -UserId $userObject.Id -Select mailboxSettings,mail -EA Stop } catch { if ($_.Exception.StatusCode -eq "Forbidden") { $userObject.UserPrincipalName + " mailbox exists in O365" } if ($_.Exception.StatusCode -eq "NotFound") { if ($userObject.mail.length -eq 0) { $userObject.UserPrincipalName + " no mailbox" } else { $userObject.UserPrincipalName + "mailbox exists on-prem" } } } |
Update: Follow-up post here.