0

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

}

kgjac
  • 5
  • 1
  • 4
  • 2
    Can you show us the definition of `Get-Is-Client-Cert-Required`? Most likely some method call in that function emits a value – Mathias R. Jessen Jan 08 '17 at 22:19
  • 2
    @MatthewWetmore `[bool]$var = Get-Stuff` is a dangerous "workaround" - an array of count > 1 will *always* result in `$true`, even if the intended value was `$false` – Mathias R. Jessen Jan 08 '17 at 22:26
  • You know, that probably explains a problem I had a while back. :) Better suggestion? There are bits I love about PowerShell, and ones that make my already sparse hair fall out. – Matthew Wetmore Jan 08 '17 at 22:32
  • Write Pester tests for your functions, make sure they don't emit useless garbage :-) – Mathias R. Jessen Jan 08 '17 at 22:36
  • @MathiasR.Jessen, yeah, well there's that. :) However, I just tried it and it seems to do what I expected. function foo { 1; 2 } [bool] $r = foo - and I get failed conversion errors as I would have expected. – Matthew Wetmore Jan 08 '17 at 22:46
  • 1
    @MatthewWetmore yes, my bad. Meant casting the ambiguous expression, like `$r = [bool](foo)`, which would be $true always if `foo` returns > 1 item – Mathias R. Jessen Jan 09 '17 at 00:18
  • thanks for the help. I have added Get-Is-Client-Cert-Required function to the initial question. – kgjac Jan 09 '17 at 01:31
  • My bad. My initial comment is completely backwards. I was expecting a bool and the function returns a bool when assigned to a NoteProperty it is converted to an array. Duh! – kgjac Jan 09 '17 at 01:39
  • Nothing obvious in there - have you verified that `Check-IIS-Express-SSL-Config $sslFlagMask` returns exactly one `[bool]` – Mathias R. Jessen Jan 09 '17 at 01:58
  • Also, avoid attempting to "declare" variables like `[System.Xml.XmlElement]$clientCertMappingNode` - that's one of the safest ways of ending up in your situation – Mathias R. Jessen Jan 09 '17 at 02:04
  • Thanks for the tip! Yup, I am using ISE I inspect the $clientCertsRequired variable before and after the assignment and it shows up as a bool with either $true or $false. I can't see what's wrong. It seems like a bug in PS but it's hard to believe. – kgjac Jan 09 '17 at 02:06
  • I will take all of the declarations out of the the Check-IIS-Express-SSL-Config function and see if that resolves the problem. **I think I see the problem now not initializing these two variables, duh!** **[system.xml.xmlelement]$clientCertMappingNode** **[system.xml.xmlelement]$iisClientCertMappingNode** – kgjac Jan 09 '17 at 02:08
  • @MathiasR.Jessen, your comment about not 'declaring' objects has resolved my problem. If you want to add this as an answer I will accept it – kgjac Jan 09 '17 at 02:21
  • I think we're all on the same page now. – Matthew Wetmore Jan 09 '17 at 04:13

1 Answers1

0

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
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206