Functions/Public/Find-FIDOKey.ps1

<#
.SYNOPSIS
    Find FIDO keys eligible for attestation with Entra ID.

.DESCRIPTION
    This function retrieves FIDO keys from a JSON file and filters them based on the provided criteria.
    The function supports filtering by Brand, Type (Bio, USB, NFC, BLE), AAGUID, TypeFilterMode, DetailedProperties, and AllProperties.
    The results can be displayed in a table, list format, or pipe.

.PARAMETER Brand
    Filter the FIDO keys by Brand. The available brands are listed in the ValidateSet.

.PARAMETER Type
    Filter the FIDO keys by Type. The available types are Bio, USB, NFC, and BLE.

.PARAMETER AAGUID
    Filter the FIDO keys by AAGUID. The AAGUIDs can be provided as an array of strings.

.PARAMETER AAGUIDFile
    Filter the FIDO keys by AAGUIDs imported from a file. Supported file formats are .txt, .csv, and .xlsx.

.PARAMETER View
    Specify the view format for the results. The available options are Table and List

.PARAMETER TypeFilterMode
    Specify the type filter mode. The available options are AtLeastTwo, AtLeastOne, AtLeastThree, and All.

.PARAMETER AllProperties
    Include all properties of the FIDO keys in the output as Json.

.PARAMETER DetailedProperties
    Include detailed properties of the FIDO keys in the output.

.EXAMPLE
    Find-FIDOKey -Brand "Yubico" -Type "USB" -View "Table"
    Find FIDO keys from the Yubico brand that support USB and display the results in a table format.

.EXAMPLE
    Find-FIDOKey -AAGUID "12345678" -View "List"
    Find FIDO keys with the specified AAGUID and display the results in a list format.

.EXAMPLE
    Find-FIDOKey -AAGUIDFile "AAGUIDs.txt" -View "Table"
    Find FIDO keys with AAGUIDs imported from a text file and display the results in a table format.

.EXAMPLE
    Find-FIDOKey -DetailedProperties | Select-Object Vendor, Description, @{Name="ProtocolFamily";Expression={$_.metadataStatement.protocolFamily}} | fl
    Find FIDO keys and show the standard properties with version from FIDO Alliance metadata.
    #>

function Find-FIDOKey {
    [CmdletBinding()]
    param (

        [Parameter()]
        [ValidateSet("ACS", "Allthenticator", "Arculus", "AuthenTrend", "Atos", "authenton1", "Chunghwa Telecom",
            "Crayonic", "Cryptnox", "Egomet", "Ensurity", "eWBM", "Excelsecu", "Feitian", "FIDO KeyPass", "FT-JCOS",
            "Google", "GoTrust", "HID Global", "Hideez", "Hypersecu", "HYPR", "IDCore", "IDEMIA", "IDmelon", "Thales",
            "ImproveID", "KEY-ID", "KeyXentic", "KONAI", "NEOWAVE", "NXP Semiconductors", "Nymi", "OCTATCO", "OneSpan",
            "OnlyKey", "OpenSK", "Pone Biometrics", "Precision", "RSA", "SafeNet", "Yubico", "Sentry Enterprises",
            "SmartDisplayer", "SoloKeys", "Swissbit", "Taglio", "Token Ring", "TOKEN2", "Identiv", "VALMIDO", "Kensington",
            "VinCSS", "WiSECURE")]
        [string[]]$Brand,

        [Parameter()]
        [ValidateSet("Bio", "USB", "NFC", "BLE")]
        [string[]]$Type,

        [Parameter(
            Position = 0,
            ValueFromPipeline = $true
        )]
        [string[]]$AAGUID,

        [Parameter()]
        [string[]]$AAGUIDFile,

        [Parameter()]
        [ValidateSet("Table", "List")]
        [string]$View,

        [Parameter()]
        [ValidateSet("AtLeastTwo", "AtLeastOne", "AtLeastThree", "All")]
        [string]$TypeFilterMode = "AtLeastOne",

        [parameter()]
        [switch]$AllProperties,

        [Parameter()]
        [switch]$DetailedProperties
    )

    # Begin block for initialization
    Begin {
        # Initialize an array to collect all AAGUIDs
        $allAAGUIDs = @() 
    }

    # Process block to handle pipeline input
    Process {
        # Collect the pipeline input AAGUIDs
        if ($AAGUID) {
            $allAAGUIDs += $AAGUID
        }
    }

    # End block for processing and output
    End {
        # Include AAGUIDs provided via the -AAGUID parameter
        if ($PSBoundParameters.ContainsKey('AAGUID')) {
            $allAAGUIDs += $PSBoundParameters['AAGUID']
        }

        # If AAGUIDFile is provided, import the AAGUIDs from the file
         if ($AAGUIDFile) {
            if (-Not (Test-Path -Path $AAGUIDFile)) {
                Write-Error "The AAGUID file was not found at path: $AAGUIDFile"
                return
            }
            else {
                $extension = [IO.Path]::GetExtension($AAGUIDFile).ToLowerInvariant()
                switch ($extension) {
                    '.txt' {
                        $fileContent = Get-Content -Path $AAGUIDFile
                    }
                    '.csv' {
                        $fileContent = Import-Csv -Path $AAGUIDFile | Select-Object -ExpandProperty AAGUID
                    }
                    '.xlsx' {
                        # Check if ImportExcel module is installed
                        if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
                            Write-Host "The 'ImportExcel' module is required to import .xlsx files."
                            Write-Host "Please install it by running 'Install-PSResource -Name ImportExcel -Scope CurrentUser'"
                            return
                        }
                        else {
                            # Import the module
                            Import-Module ImportExcel -ErrorAction Stop
                            # Import data from the .xlsx file
                            $fileContent = Import-Excel -Path $AAGUIDFile | Select-Object -ExpandProperty AAGUID
                        }
                    }
                    default {
                        Write-Error "Unsupported file extension: $extension. Supported extensions are .txt, .csv, .xlsx."
                        return
                    }
                }

                $fileAAGUIDs = $fileContent | Where-Object { -Not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() }

                # Combine AAGUIDs from file with any existing AAGUIDs
                $allAAGUIDs += $fileAAGUIDs
            }
        }

        # Remove duplicate AAGUIDs if any
        $allAAGUIDs = $allAAGUIDs | Select-Object -Unique

        # Load existing FIDO keys from JSON file
        $parentDir = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
        $jsonFilePath = Join-Path -Path $parentDir -ChildPath "Assets/FidoKeys.json"

        if (-Not (Test-Path -Path $jsonFilePath)) {
            Write-Error "The JSON file was not found at path: $jsonFilePath"
            return
        }

        $data = Get-Content -Raw $jsonFilePath | ConvertFrom-Json
        $metadata = $data.metadata
        $results = $data.keys

        # Filter by AAGUID if provided
        if ($allAAGUIDs) {
            $results = $results | Where-Object {
                $allAAGUIDs -contains $_.AAGUID
            }
        }
        
        # Filter by Brand if provided
        if ($Brand) {
            $results = $results | Where-Object {
                $Brand -contains $_.Vendor -or ($Brand | ForEach-Object { $_ -in $_.Description })
            }
        }

        # Filter by Type if provided
        if ($Type) {
            $results = $results | Where-Object {
                $typeCount = 0
                if ($Type -contains "Bio" -and $_.Bio -eq "Yes") { $typeCount++ }
                if ($Type -contains "USB" -and $_.USB -eq "Yes") { $typeCount++ }
                if ($Type -contains "NFC" -and $_.NFC -eq "Yes") { $typeCount++ }
                if ($Type -contains "BLE" -and $_.BLE -eq "Yes") { $typeCount++ }

                switch ($TypeFilterMode) {
                    "AtLeastTwo" { $typeCount -ge 2 }
                    "AtLeastThree" { $typeCount -ge 3 }
                    "All" { $typeCount -eq $Type.Count }
                    default { $typeCount -ge 1 }
                }
            }
        }

        # Sort the results by Vendor in alphabetical order
       $results = $results | Sort-Object -Property Vendor

        if ($View) {
            if ($results.Count -gt 0) {
                Write-Host "FIDO Devices eligible for attestation with Entra ID: $($results.Count)"
                Write-Host "Database Last Updated: $($metadata.databaseLastUpdated)"
                if ($View -eq "Table") {
                    if ($DetailedProperties -or $FormattedProperties) {
                        # Directly output the results
                        $results | Format-Table -AutoSize
                    } elseif ($AllProperties) {
                        $results | ConvertTo-Json -Depth 10 | Out-String | Write-Host
                    } else {
                        $results | Format-Table -Property Vendor, Description, AAGUID, Bio, USB, NFC, BLE, Version, ValidVendor -AutoSize
                    }
                } else {
                    if ($DetailedProperties) {
                        # Directly output the results
                        $results | Format-List
                    } elseif ($AllProperties) {
                        $results | ConvertTo-Json -Depth 10 | Out-String | Write-Host
                    } else {
                        $results | Select-Object Vendor, Description, AAGUID, Bio, USB, NFC, BLE, Version, ValidVendor
                    }
                }
            } else {
                Write-Host "No devices found matching the criteria."
            }
        } else {
            if ($DetailedProperties) {
                # Return the results directly
                return $results
            } elseif ($AllProperties) {
                return $results | ConvertTo-Json -Depth 10
            } else {
                return $results | Select-Object Vendor, Description, AAGUID, Bio, USB, NFC, BLE, Version, ValidVendor
            }
        }
    }
}