Azure Cloud Shell is a great idea, but it's that weird cousin no-one wants to associate with.
A perfect example is try accessing it with Device Code Flow blocked on the tenant and wanting to do something pretty simple, like view an Exchange mailbox using Get-EXOMailbox, and look at command output using your regional settings.
Microsoft's answer to the above is "get f*cked, this won't work out of the box and have fun deciphering the errors and navigating the wrong answers served up by AI chatbots". This is because at the end of the day, Microsoft don't properly test workflows when updating siloed components, nor do they document systems and components and workflows well, nor do they properly obsolete/deprecate their documentation (which means AI chatbot answers can suck hard). Unfortunately bolting on Copilot to everything in a vain attempt to make everything suck less won't work until they fix the primary root cause, which is concise + consistent + up-to-date documentation which is structured well.
The core problem is that the Az module is pre-loaded, in an effort to help you do something useful from the get-go. This is good, as it's pretty feature rich for most of the Azure components, but sucks if you're primarily trying to administer M365 resources such as Exchange Online or PowerShell.
First off, at some point you're going to see the following warning:
WARNING: You're using Az version x.x.x. The latest version of Az is y.y.y. Upgrade your Az modules using the following commands:
Update-PSResource Az -WhatIf -- Simulate updating your Az modules.
Update-PSResource Az -- Update your Az modules.And the thing to realise here is this is meaningless in Azure Cloud Shell, as you cannot update the Az module. This should be an easy fix for the Az module maintainers by adding a test for $ENV:POWERSHELL_DISTRIBUTION_CHANNEL being set to 'CloudShell' and not printing the warning, but apparently this has been too difficult since it was reported in December 2024 and is still an open issue :-(
The next problem here is that launching 'Connect-ExchangeOnline' throws this really helpful error:
OperationStopped: ObjectStore transient error: Query executes failed. BatchStatusCode: PartialSuccess. Error message: .
I'll save you the hassle of figuring this out - the error's being raised because Cloud Shell can't launch an MFA-aware login dialog in a browser for you. Such a helpful error message. Even as a programmer it's completely and utterly useless, so whoever was responsible for that really shouldn't have been paid that week/month they were working on it. Complete incompetence.
It took a few extra chatbot prompts (in Google Gemini, because Copilot was being obtuse) to figure out how to get around this. The fix was to add -DisableWAM to the command:
Connect-ExchangeOnline -DisableWAM
And finally I was able to get a Powershell session to Exchange Online, although I did have to exempt the logged in user from the Conditional Access Policy blocking Device Code Flow first (a whole other blog post on how craptacular Conditional Access can be).
Right, time to examine those mailboxes - this should be easy!
Yeah, no.
This is what I got instead when trying to run 'Get-EXOMailbox':
Get-EXOMailbox: Could not load file or assembly 'Microsoft.OData.Core, Version=7.22.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)Sigh, of course. I mean just working cleanly out of the box would be too much to ask for.
Again, Copilot started me on a wild goose chase, so I gave up on that and used Gemini again.
Gemini figured out the error was due to the fact that the Az module had loaded an older version of Microsoft.OData.Core, and that the ExchangeOnlineManagement module was unable to load the newer version.
After some persistence by telling Gemini to ignore the -UserPrincipalName parameter and to add the -DisableWAM parameter, Gemini was able to provide me with a function that launches PowerShell as a child process with no profile, allowing me to load the ExchangeOnlineManagement module cleanly without the Az module pollution, and to enter the interactive prompt in the child process.
I then fleshed out the function to get rid of the idiotic US date formats and to also bring back PSReadLine, because no-one wants to revisit their 80's tty experience :-)
With all that said and done, here is the function:
function Connect-EXOClean { pwsh -NoProfile -Command { # Set culture to en-AU $newc = [System.Globalization.CultureInfo]::new("en-AU") [System.Threading.Thread]::CurrentThread.CurrentCulture = $newc [System.Threading.Thread]::CurrentThread.CurrentUICulture = $newc [System.Globalization.CultureInfo]::CurrentCulture = $newc # PSReadLine is kinda handy Import-Module PSReadLine Import-Module ExchangeOnlineManagement -ErrorAction SilentlyContinue try { Write-Host "Connecting to Exchange Online (WAM Disabled)..." -ForegroundColor Cyan Connect-ExchangeOnline -DisableWAM Write-Host "`n--- ISOLATED SESSION ACTIVE ---" -ForegroundColor Green Write-Host "Type 'exit' to disconnect and return to Cloud Shell." -ForegroundColor Yellow # Enter the interactive sub-prompt $Host.EnterNestedPrompt() } finally { # This block runs even if the session crashes or you exit the nested prompt Write-Host "`nClosing Exchange session safely..." -ForegroundColor Gray Disconnect-ExchangeOnline -Confirm:$false } } }
To use, type in 'Connect-EXOClean', and when you're finished, type in 'exit'.
Feel free to change 'en-AU' for your local region/culture, so your EXO commands return dates/times in your format and not en-US.
Hope this helps others who'd like to use Azure Cloud Shell for M365 management without constantly having to fight it.
No comments:
Post a Comment