Public/Metrics/Get-PiHoleQuery.ps1

function Get-PiHoleQuery {
    <#
.SYNOPSIS
Get queries

.DESCRIPTION
Request individual query log entries, with optional filtering. Returns the most recent 100
queries by default; use -Length for more, and -Cursor (the Id of the oldest query already
retrieved) to page further back. Use Get-PiHoleStatsQuerySuggestions to discover valid values
for the filter parameters.

.PARAMETER PiHoleServer
The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"

.PARAMETER Password
The API Password you generated from your PiHole server

.PARAMETER From
Only return queries from this Unix timestamp onward

.PARAMETER Until
Only return queries up to this Unix timestamp

.PARAMETER Length
Number of results to return. Defaults to 100

.PARAMETER Start
Offset from the first record

.PARAMETER Cursor
Database Id of the most recent query to start from - pass the previous call's oldest returned
Id here to page further back in time

.PARAMETER Domain
Only return queries for this domain. Wildcards ("*") are supported

.PARAMETER ClientIp
Only return queries from this client IP address. Wildcards ("*") are supported

.PARAMETER ClientName
Only return queries from this client hostname. Wildcards ("*") are supported

.PARAMETER Upstream
Only return queries sent to this upstream (or "cache", "blocklist", "permitted"). Wildcards
("*") are supported

.PARAMETER Type
Only return queries of this type (e.g. "A", "AAAA")

.PARAMETER Status
Only return queries with this status (e.g. "GRAVITY", "FORWARDED")

.PARAMETER Reply
Only return queries with this reply type (e.g. "NODATA", "NXDOMAIN")

.PARAMETER Dnssec
Only return queries with this DNSSEC status (e.g. "SECURE", "INSECURE")

.PARAMETER Disk
Set to $true to load queries from the on-disk long-term database instead of the in-memory one

.PARAMETER IgnoreSsl
Set to $true to skip SSL certificate validation

.PARAMETER RawOutput
This will dump the response instead of the formatted object - includes the pagination cursor
and total/filtered record counts alongside the queries array

.EXAMPLE
Get-PiHoleQuery -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"

.EXAMPLE
Get-PiHoleQuery -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Domain "doubleclick.net" -Length 20
    #>

    [CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/queries')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
    param (
        [Parameter(Mandatory = $true)]
        [System.URI]$PiHoleServer,
        [Parameter(Mandatory = $true)]
        [string]$Password,
        [Nullable[int]]$From,
        [Nullable[int]]$Until,
        [Nullable[int]]$Length,
        [Nullable[int]]$Start,
        [Nullable[int]]$Cursor,
        [string]$Domain,
        [string]$ClientIp,
        [string]$ClientName,
        [string]$Upstream,
        [string]$Type,
        [string]$Status,
        [string]$Reply,
        [string]$Dnssec,
        [Nullable[bool]]$Disk,
        [bool]$IgnoreSsl = $false,
        [bool]$RawOutput = $false
    )
    try {
        $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

        $QueryParams = [System.Collections.ArrayList]@()
        if ($PSBoundParameters.ContainsKey('From')) { $QueryParams.Add("from=$From") | Out-Null }
        if ($PSBoundParameters.ContainsKey('Until')) { $QueryParams.Add("until=$Until") | Out-Null }
        if ($PSBoundParameters.ContainsKey('Length')) { $QueryParams.Add("length=$Length") | Out-Null }
        if ($PSBoundParameters.ContainsKey('Start')) { $QueryParams.Add("start=$Start") | Out-Null }
        if ($PSBoundParameters.ContainsKey('Cursor')) { $QueryParams.Add("cursor=$Cursor") | Out-Null }
        if ($Domain) { $QueryParams.Add("domain=$([System.Uri]::EscapeDataString($Domain))") | Out-Null }
        if ($ClientIp) { $QueryParams.Add("client_ip=$([System.Uri]::EscapeDataString($ClientIp))") | Out-Null }
        if ($ClientName) { $QueryParams.Add("client_name=$([System.Uri]::EscapeDataString($ClientName))") | Out-Null }
        if ($Upstream) { $QueryParams.Add("upstream=$([System.Uri]::EscapeDataString($Upstream))") | Out-Null }
        if ($Type) { $QueryParams.Add("type=$([System.Uri]::EscapeDataString($Type))") | Out-Null }
        if ($Status) { $QueryParams.Add("status=$([System.Uri]::EscapeDataString($Status))") | Out-Null }
        if ($Reply) { $QueryParams.Add("reply=$([System.Uri]::EscapeDataString($Reply))") | Out-Null }
        if ($Dnssec) { $QueryParams.Add("dnssec=$([System.Uri]::EscapeDataString($Dnssec))") | Out-Null }
        if ($PSBoundParameters.ContainsKey('Disk')) { $QueryParams.Add("disk=$($Disk.ToString().ToLower())") | Out-Null }

        $Uri = "$($PiHoleServer.OriginalString)/api/queries"
        if ($QueryParams.Count -gt 0) {
            $Uri += "?" + ($QueryParams -join '&')
        }

        $Params = @{
            Headers              = @{sid = $($Sid) }
            Uri                  = $Uri
            Method               = "Get"
            SkipCertificateCheck = $IgnoreSsl
            ContentType          = "application/json"
        }

        $Response = Invoke-RestMethod @Params

        if ($RawOutput) {
            Write-Output $Response
        }
        else {
            $ObjectFinal = foreach ($Item in $Response.queries) {
                [PSCustomObject]@{
                    Id       = $Item.id
                    Time     = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.time).LocalTime
                    Type     = $Item.type
                    Domain   = $Item.domain
                    Cname    = $Item.cname
                    Status   = $Item.status
                    Client   = [PSCustomObject]@{
                        Ip   = $Item.client.ip
                        Name = $Item.client.name
                    }
                    Dnssec   = $Item.dnssec
                    Reply    = [PSCustomObject]@{
                        Type = $Item.reply.type
                        Time = $Item.reply.time
                    }
                    ListId   = $Item.list_id
                    Upstream = $Item.upstream
                    Ede      = [PSCustomObject]@{
                        Code = $Item.ede.code
                        Text = $Item.ede.text
                    }
                }
            }
            Write-Output $ObjectFinal
        }
    }

    catch {
        Write-Error -Message $_.Exception.Message
    }

    finally {
        if ($Sid) {
            Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
        }
    }
}