Functions/Public/Get-CDBItem.ps1

using namespace System.Management.Automation

#These classes are a necessary workaround to use variables as validate sets. Hopefully Microsoft bakes this in someday.
class ValidSubClassGenerator : IValidateSetValuesGenerator {
    [string[]] GetValidValues() {
        return ($Script:SubClassURIs).Keys
    }
}

<#
.Synopsis
   Returns item(s) from CDB given criteria.
.DESCRIPTION
   Returns item(s) from CDB given criteria.
.PARAMETER SubClass
    The specific type of item. Ex: building, network, service, etc.
    Tab completion is supported.
.PARAMETER Filter
    An optional set of filters for results. Properties for a given SubClass can be found with Get-CDBSubclassSchema.
.PARAMETER Limit
    Limit on results returned. The stock default is 20 and this is controled via the settings.json of the module. CDB hard caps this at 1000.
.PARAMETER Id
    The specific Id of the item you are looking for.
.PARAMETER Recursive
    Attempt to resolve properties of objects that are links to other CDB items. Can only be used with Id and NetworkByHostIP since this can be intensive on CDB with a high result count.
.PARAMETER ReturnAll
    Returns all items of the given SubClass from CDB.
.PARAMETER NetworkByHostIP
    Returns the network item that the given IP address belongs to. Supports both IPv4 and IPv6.
.EXAMPLE
   Get-CDBItem -id 1770

   This will return the specific item with Id 1770
.EXAMPLE
   Get-CDBItem -SubClass system -Filter 'ipv4_address=64.22.187.105'

   This will return a system with the IP of 64.22.187.105. Keep in mind CDB does not allow filtering on all properties.
.EXAMPLE
   Get-CDBItem -NetworkByHostIP '64.22.187.105'

   This will return the network that '64.22.187.105' falls in.
#>

function Get-CDBItem {
    [CmdletBinding(DefaultParametersetname='Id')]
    param (
        [Parameter(Mandatory=$true,ParameterSetName = 'Filter')]
        [ValidateSet( [ValidSubClassGenerator] )]
        [String]$SubClass,

        [Parameter(ParameterSetName = 'Filter')]
        [String[]]$Filter,

        [Parameter(ParameterSetName = 'Filter')]
        [int]$Limit = $Script:Settings.DefaultReturnLimit,

        [Parameter(ParameterSetName = 'Filter')]
        [Switch]$ReturnAll,

        [parameter(ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'Id')]
        [int]$Id,

        [Parameter(ParameterSetName = 'NetworkByHostIP')]
        [String]$NetworkByHostIP,

        [Parameter(ParameterSetName = 'Id')]
        [Parameter(ParameterSetName = 'NetworkByHostIP')]
        [Switch]$Recursive
    )

    begin {

    }

    process {
        if($NetworkByHostIP){
            #This is possible by manually specifying the filter but its tedious and unintuitive for the user so we do the work for them as this is a very common use case.
            #The IP address family (v4 or v6) is determined and the filter genereted.
            $ResolvedAddress = Assert-IPAddress -IPAddress $NetworkByHostIP
            Write-Verbose -Message "IPAddress resolved to $($ResolvedAddress.IPAddress) of address family $($ResolvedAddress.AddressFamily)."

            $Filter = (
                "ip$($ResolvedAddress.AddressFamily)_address_low__lte=$($ResolvedAddress.IPAddress)",
                "ip$($ResolvedAddress.AddressFamily)_address_high__gte=$($ResolvedAddress.IPAddress)"
            )

            $SubClass = 'network'
        }

        if($Limit -gt 1000){
            Write-Warning -Message 'CDB only supports limits up to 1000. Consider using -ReturnAll to get the full collection of items.'
        }

        $Return = [System.Collections.ArrayList]@()

        if($Id){
            #All items have an ID that is accesbible via the item directory but these just return the specific subclass URI for the item.
            #The actual item properties are not stored on this it is just a redirect to the actual subclass with the information.
            #So when looking up a specific item we have to run two calls.
            $Redirect = (Invoke-CDBRestCall -RelativeURI "/api/v2/item/$($Id)/").subclass
            $Return += Invoke-CDBRestCall -RelativeURI $Redirect
        }
        elseif($ReturnAll){
            if($PSBoundParameters.Keys -contains 'Limit'){
                Write-Warning -Message 'Ignoring provided limit since ReturnAll was also specified.'
            }

            #CDB has a hard cap of 1000 items returned in a single call. So to get all results we have to first get the total count from the meta data and then iterate through batches of 1000 results using the offset parameter.
            [int]$TotalObjects = (Invoke-CDBRestCall -RelativeURI $Script:SubClassURIs[$SubClass].list_endpoint -Limit 1).meta.total_count
            Write-Verbose -Message "$($TotalObjects) total objects of subclass $($SubClass) available."
            [int]$Offset = 0
            While($TotalObjects -gt 0){
                $Return += (Invoke-CDBRestCall -RelativeURI $Script:SubClassURIs[$SubClass].list_endpoint -Filter $Filter -Limit 1000 -Offset $Offset).Objects
                $Offset += 1000
                $TotalObjects -= 1000
            }
        }
        else{
            $Return += (Invoke-CDBRestCall -RelativeURI $Script:SubClassURIs[$SubClass].list_endpoint -Filter $Filter -Limit $Limit).Objects
        }

        if($Recursive){
            #This currently only works for top level URIs not something like contacts that is an array of JSON objects. It will find all properties that are relative URIs then replace them with the object they link to.
            foreach($Item in $Return){
                $Item.psobject.Properties | Where-Object -FilterScript {$_.value -like "/api/v2/*" -and $_.value -ne $Item.resource_uri} | ForEach-Object -Process {
                    $Item.$($_.Name) = Invoke-CDBRestCall -RelativeURI $_.value
                }
            }

            $Item.contacts = ($Item.contacts).contact.trimstart('/api/v2/item/').trim('/') | Get-CDBItem
        }

        if($Return){
            $Return
        }
        Else{
            throw "No CDB results for the provided parameters."
        }
    }

    end {

    }
}