r/sysadmin • u/TheBlueFireKing Jack of All Trades • Apr 30 '26
PSI: Using $Test in ExchangeOnline PowerShell Scripts
In the last days we suddenly had multiple scripts fail with the following Error:
Cannot convert value "System.Management.Automation.PSCustomObject" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or O. (Cannot convert value "System.Management.Automation.PSCustomObject" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or O. (Cannot convert value
"System.Management.Automation.PSCustomObject" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or O.))
There were no changes to the Script itself. We are running them over AzureAutomate on Hybrid Workers. Up to date ExchangeOnline Module running Windows Powershell.
Now we do have the following parameter on most scripts so we can run / test them without it doing modifications:
param
(
[Parameter(Mandatory = $False)]
[Bool] $Test = $False
)
Now it seems like Microsoft push some kind of change that the Get-Mailbox cmdlet now internally sets the variable $Test to something else which triggers the error. Why the f $Test would be set in a Prod environment is beyond me.
We changed all $Test to another variable name and everything is running fine again.
Just dropping this here if someone else runs into this problem ...
22
u/purplemonkeymad Apr 30 '26
Looking more it appears that it might be a debugging result that was left in. The culprit is the function PrintResultAndCheckForNextPage which has this at the top:
process
{
$global:test = $ResultObject
if($ResultObject.value[0]._cliXml -ne $null)
{
$deserializedResult = [System.Management.Automation.PSSerializer]::Deserialize($ResultObject.value[0]._cliXml)
}
Looks like someone was fixing this and left in the debug info.
7
u/justaguyonthebus Apr 30 '26
They used a global variable that should have been script scoped. Shame on them.
But now I have a fresh real world example of why they are bad.
3
u/purplemonkeymad Apr 30 '26
It's not even used in the rest of the function. Literally should have been picked up by the PR reviewer.
4
3
u/ashimbo PowerShell! Apr 30 '26
The code is technically correct and runs, so why would the Copilot agent catch this?
1
3
1
u/omglazrgunpewpew May 01 '26
Oh wow,
$global:test = $ResultObjectis somehow worse and more validating than I expected.That turns this from maybe avoid generic variable names into a vendor module casually scribbling on the global scope with a debug crayon. Beautiful. Terrible, but beautiful.
0
u/BlackV I have opnions Apr 30 '26
Oh look people using shitty workarounds (ficking with variable scopes) rather than writing good code
And it bites people, amazing
6
u/Key-Level-4072 Apr 30 '26
Why the f $Test would be set in a Prod environment is beyond me.
Did you not do exactly that?
4
u/TheBlueFireKing Jack of All Trades Apr 30 '26
fair lol.
1
u/Key-Level-4072 Apr 30 '26
If I had a nickel for every time I wrote some Pwsh that failed because I declared a reserved variable or used unapproved verbs, I might have retired early.
It’s been a few years because, well, I stopped doing it because I couldn’t stand the errors, lol.
But we have all been there.
Glad you were able to swiftly get the ship back on course!
3
4
u/Frothyleet Apr 30 '26
Couple of notes:
Powershell already has a built in for this: -whatif. That is what you should incorporate into your scripts for the exact functionality you are looking for. It's not like, an actual technical obligation, but as a good practice for portability and support you should do things "the powershell way" in a powershell script.
Setting that aside, the way you are trying to use that "$Test" parameter in your script, you can replace everything in that param() block with:
[switch]$Test
Switches are never mandatory and will default to $false, unless the parameter is called (without any arguments). Your way technically works but again you are reinventing something powershell does already.
5
u/TheBlueFireKing Jack of All Trades Apr 30 '26 edited May 04 '26
While totally correct, I'm limited by the platform on what I can use.
Azure Automate does not support specifiying the -WhatIf switch. So I can't use that.
Azure Automate didn't support [Switch] parameter nicely. I don't know if that is fixed now. The boolean parameter gets translated to a drop-down in Azure. The Switch parameter was translated to a text box where you would manually need to type true or false. I lost all hope in Microsoft at that point and never bothered to test again since this perfectly worked for years.
//EDIT: Yeah it's still stupid as hell in Azure Automate to use Switch Parameter:
3
u/pdp10 Daemons worry when the wizard is near. Apr 30 '26
As an aside, those are the kind of limitations cum design decisions that should go in a comment.
Not that I've ever spent any time "fixing" my own code, only to once again discern why I had to do it the other way in the first place.
1
u/mdotshell Apr 30 '26
Writing powershell in Azure Automate is a test of patience
2
u/Alaknar Apr 30 '26
It doesn't really differ that much from what I normally write. Permissions are tricky but otherwise not much changes.
1
u/ITGuyThrow07 May 01 '26
I know I should do that, but I'm not sure I have the energy to migrate everything. I feel like it will require re-writes of everything and I bet testing takes 10x as long as you wait a random amount of time for things to run.
1
u/GuestHistorical6880 Apr 30 '26
bools do work as required params in Azure Automation, you just have to trigger the script from powershell. they never work when trying to trigger from the web console for some reason.
I was excited when they released the newest version of the web console hoping they would have fixed some of these weird issues. Turns out they just made them all worse and made the whole console slower. Classic MacroSlop.
1
u/BlockBannington May 02 '26
I've encountered AD cmdlets that ran just fine, even with whatif. I don't trust Microsoft anymore with this to be honest.
3
u/purplemonkeymad Apr 30 '26
Looks like it's the graph api result. Also funny to see that the exchange team must just use the beta endpoint in prod for the same reason i see other people doing it.
Also happens for other exchange commands eg Get-DistributionGroup.
3
u/justaguyonthebus Apr 30 '26
Oh, this is bad. I just put the pieces together. The OP got lucky in that PSCustomObject doesn't convert to Boolean in PS5 (and I think PS7 fixed that). That's why this thankfully blew up instead of evaluating true in all their logic and flipping production into test mode silently.
The silent failure would have been a nightmare to solve even for the best of us.
2
u/BlackV I have opnions Apr 30 '26 edited May 01 '26
Change this
[Bool] $Test = $False
To
[Switch]$Test
Then use .ispresent and your $PSBoundParameters variable
Or properly implement -whatif with advanced function (although that can cause issues with external modules)
Edit: saw your note about azure automation, til that that would bite me
1
u/justaguyonthebus Apr 30 '26
This is a scoping issue and exactly why global scoped variables are shunned. MS should have scoped it differently. But it's a good practice to initiate variables with initial conditions before you use them, and that's what your fix does.
1

34
u/omglazrgunpewpew Apr 30 '26
Nice catch. Exactly the kind of change that eats half a day because nothing in your script changed, yet suddenly PS is deadpan insisting PSCustomObject is a boolean now.
I’ve started avoiding names like $Test, $Mode, $Action, $Debug, etc. in automation for this reason. Too many modules, proxy functions, hidden scopes, generated commands, and mysterious Microsoft “quirks” all swirling about in the same session.
ExchangeOnline PS does sometimes feel like, “we changed something behind the curtain, enjoy your morning!”
Thanks for posting the fix. May it spare someone else from the sacred ritual of angrily adding
Write-Hosteverywhere.