Search code examples
powershellpsobject

Boolean NoteProperty becomes an Array


The title says it all single boolean value becomes an array when assigned to a NoteProperty using Add-Member or using splatting.

PSVersion: 5.0.1xx

I have what I consider a strange problem. I am creating a PSObject with one of the NoteProperty members as a boolean. The function loops through a list, calls a function to perform an evaluation, creates an object and then adds it to an array. This seems to only happen to the first object created but I have not tested this with 5 or more objects being created.

I have validated that the functions are actually returning bool and that the variable being assigned to the property is an bool.

My workaround seems solid but am curious as to why this is happening.

Here's part of the code:

$clientCertsRequired = Get-Is-Client-Cert-Required -configFile $configFile -siteName $siteName

$httpsStatus = "Https is not enabled"
$clientCertStatus = "Client certs are not required"

if ($httpsEnabled -eq $true) {
    $httpsStatus = "Https is enabled"
}

if ($clientCertsRequired -eq $true){
    $clientCertStatus = "Client certs are required"
} 

$sc = New-Object PSObject -Property @{
    SiteName = $siteName;
    ConfigFilePath = $path;
    HttpsEnabled = $httpsStatus;
    ClientCertStatus =$clientCertStatus; 
    ClientCertRequired = $clientCertsRequired;
}

# clean up of some inexplicable problem where assignment to property 
# produces array with actual value in the last element.
if ($sc.ClientCertRequired.GetType().Name -eq "Object[]"){
    $sc.ClientCertRequired = $sc.ClientCertRequired[-1]
}

$si += $sc

Function Get-Is-Client-Cert-Required{
param(
    [xml]$configFile, 
    [string]$siteName
)
$functionName = $MyInvocation.MyCommand.Name  

$clientCertRequired = $false
try{
    # then read locations section (this will often not have any pages 
    $locationPath = "//configuration/location[@path='$siteName']"
    [system.xml.xmlelement]$location = $configFile.DocumentElement.SelectSingleNode($locationPath)

    if($location -ne $null){
        [system.xml.xmlelement]$accessNode = $location.SelectSingleNode("system.webServer/security/access")
        [system.xml.xmlelement]$authenticationNode = $location.SelectSingleNode("system.webServer/security/authentication")
        [system.xml.xmlelement]$clientCertMappingNode
        [system.xml.xmlelement]$iisClientCertMappingNode

        [int]$sslFlagMask = 0
        if($accessNode -ne $null){
            $sslFlags =  $accessNode.Attributes.GetNamedItem("sslFlags")
            # $sslFlags = $accessNode.Attributes["sslFlags"].Value
            if($sslFlagMask -ne $null){
                $sslFlagMask = Convert-Ssl-Flag-String-To-Int-Flag -sslFlag $sslFlags.Value
            }
        }

        if($authenticationNode -ne $null){
            [system.xml.xmlelement]$clientCertMappingNode = $authenticationNode.SelectSingleNode("clientCertificateMappingAuthentication[@enabled='true']")
            [system.xml.xmlelement]$iisClientCertMappingNode = $authenticationNode.SelectSingleNode("iisClientCertificateMappingAuthentication[@enabled='true']")
        }

        $clientCertAccepted = ($sslFlagMask -band $certAccepted) -eq $certAccepted
        $clientCertRequired = Check-IIS-Express-SSL-Config $sslFlagMask

        if($clientCertRequired -eq $false){
            if($clientCertAccepted -and ($clientCertMappingNode -ne $null -or $iisClientCertMappingNode -ne $null)){
                $clientCertRequired = $true
            }
        }
    }
}catch{
    $exceptionMessage = Get-Formatted-Exception-String -exceptionObject $_
    $message = "$functionName - Exception`: $exceptionMessage"
    Add-Exception -exception $message
    Log-Error -message $message 
}

$clientCertRequired

}


Solution

  • In the body of the Get-Is-Client-Cert-Required function, you do:

    [system.xml.xmlelement]$clientCertMappingNode
    [system.xml.xmlelement]$iisClientCertMappingNode
    

    This pattern:

    [type]$nonExistingVariable
    

    Is a terrible idea in PowerShell - unlike C#, PowerShell does not have the concept of bare variable declarations, and the above pattern simply casts $null to the specified type, emitting a new instance of said type if it succeeds - this is likely what causes the function to output an array.


    If you really need to bind a variable to a specific type, cast on assignment:

    [type]$Variable = Get-Stuff
    

    Bonus tip: The PowerShell-idiomatic naming convention for functions and cmdlets is Noun-Verb, with only a single hyphen. A more appropriate name for the function would be:

    Test-ClientCertRequirement