DNSFilterAPI.psm1

#Region './prefix.ps1' -1

# Enable TLS 1.2 for API calls
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
#EndRegion './prefix.ps1' 3
#Region './Private/Invoke-DNSFilterRequest.ps1' -1

function Invoke-DNSFilterRequest {
    <#
    .SYNOPSIS
        Makes an HTTP request to the DNSFilter API.

    .DESCRIPTION
        Internal helper function that handles session validation, URI construction,
        JSON serialization, and error handling for all DNSFilter API calls.

        Accepts an optional -Session parameter for thread safety. When omitted,
        falls back to the module-scoped $script:DNSFilterSession.

    .NOTES
        This is a private function and should not be called directly.
        Requires Connect-DNSFilter to be called first.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [string]$Endpoint,

        [ValidateSet('Get', 'Post', 'Put', 'Delete', 'Patch')]
        [string]$Method = 'Get',

        [hashtable]$Body,

        [hashtable]$QueryParameters,

        [hashtable]$Session
    )

    # Resolve session: explicit parameter wins, then fall back to module scope
    $ResolvedSession = if ($Session) { $Session } else { $script:DNSFilterSession }

    if (-not $ResolvedSession.WebSession) {
        throw 'No active DNSFilter session. Run Connect-DNSFilter first.'
    }

    # Build URI
    $Uri = Join-Url $ResolvedSession.BaseURI $Endpoint

    # Append query parameters
    if ($QueryParameters -and $QueryParameters.Count -gt 0) {
        $QueryParts = foreach ($Key in $QueryParameters.Keys) {
            $Value = $QueryParameters[$Key]
            if ($null -eq $Value) { continue }
            '{0}={1}' -f [uri]::EscapeDataString($Key), [uri]::EscapeDataString($Value)
        }
        $Separator = if ($Uri.Contains('?')) { '&' } else { '?' }
        $Uri = $Uri + $Separator + ($QueryParts -join '&')
    }

    $RequestParams = @{
        Uri             = $Uri
        Method          = $Method
        WebSession      = $ResolvedSession.WebSession
        UseBasicParsing = $true
    }

    if ($Body) {
        $RequestParams.Body = $Body | ConvertTo-Json -Depth 10
        $RequestParams.ContentType = 'application/json'
        Write-Verbose "Request body: $($RequestParams.Body)"
    }

    Write-Verbose "Invoking DNSFilter API: $Method $Uri"

    try {
        $Response = Invoke-RestMethod @RequestParams
    } catch {
        $WebException = $_
        $ErrorMessage = $WebException.Exception.Message
        if ($WebException.ErrorDetails.Message) {
            try {
                $ApiError = $WebException.ErrorDetails.Message | ConvertFrom-Json
                if ($ApiError.errors) {
                    $ErrorMessage = ($ApiError.errors | ForEach-Object { $_.detail }) -join '; '
                }
            } catch {
                $ErrorMessage = $WebException.ErrorDetails.Message
            }
        }
        throw "DNSFilter API request failed ($Method $Endpoint): $ErrorMessage"
    }

    if ($null -ne $Response.data) {
        return $Response.data
    }
    return $Response
}
#EndRegion './Private/Invoke-DNSFilterRequest.ps1' 92
#Region './Private/Join-Url.ps1' -1

function Join-Url {
    <#
    .SYNOPSIS
        Joins URL path segments, handling trailing/leading slashes.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [string]$Path,

        [Parameter(Mandatory)]
        [string]$ChildPath
    )

    "$($Path.TrimEnd('/'))/$($ChildPath.TrimStart('/'))"
}
#EndRegion './Private/Join-Url.ps1' 18
#Region './Public/Application/Get-DNSFilterApplication.ps1' -1

function Get-DNSFilterApplication {
    <#
    .SYNOPSIS
        Retrieves DNSFilter applications.

    .DESCRIPTION
        Returns active applications, all applications (including deleted), or a specific application by ID.

    .PARAMETER ID
        The ID of a specific application to retrieve.

    .PARAMETER All
        Return all applications including deleted ones.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterApplication

    .EXAMPLE
        Get-DNSFilterApplication -ID 123

    .EXAMPLE
        Get-DNSFilterApplication -All

    .OUTPUTS
        PSCustomObject. Application objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [hashtable]$Session
    )

    $Endpoint = '/v1/applications'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    Invoke-DNSFilterRequest -Endpoint $Endpoint -Session $Session
}
#EndRegion './Public/Application/Get-DNSFilterApplication.ps1' 48
#Region './Public/Billing/Get-DNSFilterBilling.ps1' -1

function Get-DNSFilterBilling {
    <#
    .SYNOPSIS
        Retrieves billing information for an organization.

    .PARAMETER OrganizationId
        The organization ID to get billing for.

    .EXAMPLE
        Get-DNSFilterBilling -OrganizationId 12345

    .OUTPUTS
        PSCustomObject. Billing information.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$OrganizationId,

        [hashtable]$Session
    )

    Invoke-DNSFilterRequest -Endpoint '/v1/billing' -QueryParameters @{
        organization_id = $OrganizationId
    } -Session $Session
}
#EndRegion './Public/Billing/Get-DNSFilterBilling.ps1' 28
#Region './Public/Connection/Connect-DNSFilter.ps1' -1

function Connect-DNSFilter {
    <#
    .SYNOPSIS
        Authenticates to the DNSFilter API and stores the session.

    .DESCRIPTION
        Establishes a session with the DNSFilter API using a Bearer API key.
        The session is stored for use by all other module functions.

        Returns the session hashtable so it can be captured and passed via -Session
        to other functions for use in parallel runspaces (ForEach-Object -Parallel).

    .PARAMETER APIKey
        The DNSFilter API key for authentication.

    .PARAMETER BaseURI
        The base URI of the DNSFilter API. Defaults to 'api.dnsfilter.com'.

    .EXAMPLE
        Connect-DNSFilter -APIKey 'your-api-key-here'

    .EXAMPLE
        $session = Connect-DNSFilter -APIKey $Key
        1..10 | ForEach-Object -Parallel {
            Get-DNSFilterNetwork -Session $using:session
        }

    .OUTPUTS
        Hashtable. The session object (also stored in module scope).
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$APIKey,

        [ValidateNotNullOrEmpty()]
        [string]$BaseURI = 'api.dnsfilter.com'
    )

    # Strip any protocol prefix and enforce https
    $CleanURI = $BaseURI -replace '^https?://', ''
    $script:DNSFilterSession = @{
        BaseURI    = "https://$CleanURI"
        WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
    }

    # Set persistent headers on the session so every subsequent request carries them
    $script:DNSFilterSession.WebSession.Headers.Add('Authorization', "Bearer $APIKey")
    $script:DNSFilterSession.WebSession.Headers.Add('Accept', 'application/json')

    $AuthParams = @{
        Uri             = Join-Url $script:DNSFilterSession.BaseURI '/v1/authenticate'
        Method          = 'Post'
        WebSession      = $script:DNSFilterSession.WebSession
        UseBasicParsing = $true
    }

    try {
        $null = Invoke-WebRequest @AuthParams
        Write-Verbose "Successfully connected to DNSFilter API at $($script:DNSFilterSession.BaseURI)"
    } catch {
        $script:DNSFilterSession = @{ BaseURI = $null; WebSession = $null }
        throw "Failed to connect to DNSFilter API: $($_.Exception.Message)"
    }

    $script:DNSFilterSession
}
#EndRegion './Public/Connection/Connect-DNSFilter.ps1' 70
#Region './Public/Connection/Get-DNSFilterSession.ps1' -1

function Get-DNSFilterSession {
    <#
    .SYNOPSIS
        Returns the current DNSFilter API session information.

    .DESCRIPTION
        Returns the base URI and whether a session object has been initialized.
        Does not expose the session cookie or credentials.

        Note: SessionExists reflects whether Connect-DNSFilter has been called,
        not whether the session token is still valid on the server.

    .EXAMPLE
        Get-DNSFilterSession

    .OUTPUTS
        PSCustomObject. Session information with BaseURI and SessionExists status.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param()

    [PSCustomObject]@{
        BaseURI       = $script:DNSFilterSession.BaseURI
        SessionExists = [bool]$script:DNSFilterSession.WebSession
    }
}
#EndRegion './Public/Connection/Get-DNSFilterSession.ps1' 28
#Region './Public/Invoice/Get-DNSFilterInvoice.ps1' -1

function Get-DNSFilterInvoice {
    <#
    .SYNOPSIS
        Retrieves DNSFilter invoices.

    .DESCRIPTION
        Returns invoices, optionally filtered by organization ID.

    .PARAMETER OrganizationId
        Filter invoices by organization ID.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterInvoice

    .EXAMPLE
        Get-DNSFilterInvoice -OrganizationId 12345

    .OUTPUTS
        PSCustomObject. Invoice objects.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [int]$OrganizationId,

        [hashtable]$Session
    )

    $RequestParams = @{ Endpoint = '/v1/invoices'; Session = $Session }
    if ($PSBoundParameters.ContainsKey('OrganizationId')) {
        $RequestParams.QueryParameters = @{ organization_id = $OrganizationId }
    }

    Invoke-DNSFilterRequest @RequestParams
}
#EndRegion './Public/Invoice/Get-DNSFilterInvoice.ps1' 39
#Region './Public/IPAddress/Get-DNSFilterIPAddress.ps1' -1

function Get-DNSFilterIPAddress {
    <#
    .SYNOPSIS
        Retrieves DNSFilter IP addresses.

    .PARAMETER ID
        The ID of a specific IP address to retrieve.

    .PARAMETER All
        Return all IP addresses.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterIPAddress

    .EXAMPLE
        Get-DNSFilterIPAddress -ID 123

    .EXAMPLE
        Get-DNSFilterIPAddress -All

    .OUTPUTS
        PSCustomObject. IP address objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [hashtable]$Session
    )

    $Endpoint = '/v1/ip_addresses'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    Invoke-DNSFilterRequest -Endpoint $Endpoint -Session $Session
}
#EndRegion './Public/IPAddress/Get-DNSFilterIPAddress.ps1' 45
#Region './Public/IPAddress/New-DNSFilterIPAddress.ps1' -1

function New-DNSFilterIPAddress {
    <#
    .SYNOPSIS
        Creates a new DNSFilter IP address entry.

    .PARAMETER OrganizationId
        The organization ID for this IP address.

    .PARAMETER NetworkId
        The network ID to associate this IP address with.

    .PARAMETER IPAddress
        The IP address to register. Mutually exclusive with Hostname.

    .PARAMETER Hostname
        A dynamic hostname to register. Mutually exclusive with IPAddress.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        New-DNSFilterIPAddress -OrganizationId 123 -NetworkId 456 -IPAddress '203.0.113.10'

    .EXAMPLE
        New-DNSFilterIPAddress -OrganizationId 123 -NetworkId 456 -Hostname 'office.example.dyndns.org'

    .OUTPUTS
        PSCustomObject. The created IP address object.
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'IPAddress')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$OrganizationId,

        [Parameter(Mandatory)]
        [int]$NetworkId,

        [Parameter(Mandatory, ParameterSetName = 'IPAddress')]
        [ipaddress]$IPAddress,

        [Parameter(Mandatory, ParameterSetName = 'Hostname')]
        [string]$Hostname,

        [hashtable]$Session
    )

    $IpEntry = @{
        organization_id = $OrganizationId
        network_id      = $NetworkId
    }
    if ($PSBoundParameters.ContainsKey('IPAddress')) { $IpEntry.address = $IPAddress.ToString() }
    if ($PSBoundParameters.ContainsKey('Hostname')) { $IpEntry.dynamic_hostname = $Hostname }

    $DisplayName = if ($PSBoundParameters.ContainsKey('IPAddress')) { $IPAddress.ToString() } else { $Hostname }

    if ($PSCmdlet.ShouldProcess($DisplayName, 'Create IP address')) {
        Invoke-DNSFilterRequest -Endpoint '/v1/ip_addresses' -Method Post -Body @{
            ip_address = $IpEntry
        } -Session $Session
    }
}
#EndRegion './Public/IPAddress/New-DNSFilterIPAddress.ps1' 63
#Region './Public/IPAddress/Remove-DNSFilterIPAddress.ps1' -1

function Remove-DNSFilterIPAddress {
    <#
    .SYNOPSIS
        Deletes a DNSFilter IP address entry.

    .PARAMETER ID
        The ID of the IP address entry to delete.

    .EXAMPLE
        Remove-DNSFilterIPAddress -ID 123

    .OUTPUTS
        PSCustomObject. Deletion result.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    if ($PSCmdlet.ShouldProcess("IP Address $ID", 'Delete IP address')) {
        Invoke-DNSFilterRequest -Endpoint (Join-Url '/v1/ip_addresses' $ID) -Method Delete -Session $Session
    }
}
#EndRegion './Public/IPAddress/Remove-DNSFilterIPAddress.ps1' 28
#Region './Public/IPAddress/Update-DNSFilterIPAddress.ps1' -1

function Update-DNSFilterIPAddress {
    <#
    .SYNOPSIS
        Updates an existing DNSFilter IP address entry.

    .PARAMETER ID
        The ID of the IP address entry to update.

    .PARAMETER IPAddress
        Updated IP address.

    .PARAMETER Hostname
        Updated dynamic hostname.

    .PARAMETER NetworkId
        Updated network ID.

    .EXAMPLE
        Update-DNSFilterIPAddress -ID 123 -IPAddress '203.0.113.20'

    .EXAMPLE
        Update-DNSFilterIPAddress -ID 123 -Hostname 'new-office.dyndns.org'

    .OUTPUTS
        PSCustomObject. The updated IP address object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [ipaddress]$IPAddress,
        [string]$Hostname,
        [int]$NetworkId,

        [hashtable]$Session
    )

    $IpEntry = @{}
    if ($PSBoundParameters.ContainsKey('IPAddress')) { $IpEntry.address = $IPAddress.ToString() }
    if ($PSBoundParameters.ContainsKey('Hostname')) { $IpEntry.dynamic_hostname = $Hostname }
    if ($PSBoundParameters.ContainsKey('NetworkId')) { $IpEntry.network_id = $NetworkId }

    $Endpoint = Join-Url '/v1/ip_addresses' $ID

    if ($PSCmdlet.ShouldProcess("IP Address $ID", 'Update IP address')) {
        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            ip_address = $IpEntry
        } -Session $Session
    }
}
#EndRegion './Public/IPAddress/Update-DNSFilterIPAddress.ps1' 53
#Region './Public/Network/Get-DNSFilterNetwork.ps1' -1

function Get-DNSFilterNetwork {
    <#
    .SYNOPSIS
        Retrieves DNSFilter networks.

    .DESCRIPTION
        Returns networks for the authenticated user, all networks, a specific network by ID,
        or networks filtered by organization.

    .PARAMETER ID
        The ID of a specific network to retrieve.

    .PARAMETER All
        Return all networks.

    .PARAMETER OrganizationId
        Filter networks by organization ID.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterNetwork

    .EXAMPLE
        Get-DNSFilterNetwork -ID 123

    .EXAMPLE
        Get-DNSFilterNetwork -All

    .EXAMPLE
        Get-DNSFilterNetwork -OrganizationId 456

    .OUTPUTS
        PSCustomObject. Network objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [Parameter(ParameterSetName = 'Organization')]
        [int]$OrganizationId,

        [hashtable]$Session
    )

    $Endpoint = '/v1/networks'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    $Results = Invoke-DNSFilterRequest -Endpoint $Endpoint -Session $Session

    if ($PSBoundParameters.ContainsKey('OrganizationId')) {
        $Results | Where-Object { $_.relationships.organization.data.id -eq $OrganizationId }
    } else {
        $Results
    }
}
#EndRegion './Public/Network/Get-DNSFilterNetwork.ps1' 64
#Region './Public/Network/New-DNSFilterNetwork.ps1' -1

function New-DNSFilterNetwork {
    <#
    .SYNOPSIS
        Creates a new DNSFilter network.

    .PARAMETER Name
        The name of the network.

    .PARAMETER OrganizationId
        The organization ID this network belongs to.

    .PARAMETER PolicyId
        The policy ID to assign to the network.

    .PARAMETER ScheduledPolicyId
        The scheduled policy ID to assign.

    .PARAMETER BlockPageId
        The block page ID to assign.

    .PARAMETER ExternalId
        An external identifier for the network.

    .EXAMPLE
        New-DNSFilterNetwork -Name 'Main Office' -OrganizationId 123

    .EXAMPLE
        New-DNSFilterNetwork -Name 'Branch Office' -OrganizationId 123 -PolicyId 456

    .OUTPUTS
        PSCustomObject. The created network object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [int]$OrganizationId,

        [int]$PolicyId,
        [int]$ScheduledPolicyId,
        [int]$BlockPageId,
        [string]$ExternalId,

        [hashtable]$Session
    )

    $ParamMap = @{
        Name              = 'name'
        OrganizationId    = 'organization_id'
        PolicyId          = 'policy_id'
        ScheduledPolicyId = 'scheduled_policy_id'
        BlockPageId       = 'block_page_id'
        ExternalId        = 'external_id'
    }

    $Network = @{}
    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Network[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    if ($PSCmdlet.ShouldProcess($Name, 'Create network')) {
        Invoke-DNSFilterRequest -Endpoint '/v1/networks' -Method Post -Body @{
            network = $Network
        } -Session $Session
    }
}
#EndRegion './Public/Network/New-DNSFilterNetwork.ps1' 73
#Region './Public/Network/New-DNSFilterNetworkSecret.ps1' -1

function New-DNSFilterNetworkSecret {
    <#
    .SYNOPSIS
        Generates a new secret key for a DNSFilter network.

    .PARAMETER ID
        The network ID to generate a secret key for.

    .EXAMPLE
        New-DNSFilterNetworkSecret -ID 123

    .OUTPUTS
        PSCustomObject. The generated secret key.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    $Endpoint = Join-Url (Join-Url '/v1/networks' $ID) 'secret_key'

    if ($PSCmdlet.ShouldProcess("Network $ID", 'Generate secret key')) {
        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Post -Session $Session
    }
}
#EndRegion './Public/Network/New-DNSFilterNetworkSecret.ps1' 30
#Region './Public/Network/Remove-DNSFilterNetwork.ps1' -1

function Remove-DNSFilterNetwork {
    <#
    .SYNOPSIS
        Deletes a DNSFilter network.

    .PARAMETER ID
        The ID of the network to delete.

    .EXAMPLE
        Remove-DNSFilterNetwork -ID 123

    .OUTPUTS
        PSCustomObject. Deletion result.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    if ($PSCmdlet.ShouldProcess("Network $ID", 'Delete network')) {
        Invoke-DNSFilterRequest -Endpoint (Join-Url '/v1/networks' $ID) -Method Delete -Session $Session
    }
}
#EndRegion './Public/Network/Remove-DNSFilterNetwork.ps1' 28
#Region './Public/Network/Set-DNSFilterLocalDomain.ps1' -1

function Set-DNSFilterLocalDomain {
    <#
    .SYNOPSIS
        Sets local domains for a DNSFilter network.

    .DESCRIPTION
        Replaces or appends local domains on a network. Use -Append to add to the
        existing list instead of replacing it.

    .PARAMETER NetworkId
        The network ID to configure.

    .PARAMETER LocalDomains
        Array of local domain names to set.

    .PARAMETER Append
        Append to existing domains instead of replacing them.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Set-DNSFilterLocalDomain -NetworkId 123 -LocalDomains 'corp.local', 'internal.lan'

    .EXAMPLE
        Set-DNSFilterLocalDomain -NetworkId 123 -LocalDomains 'new.local' -Append

    .OUTPUTS
        PSCustomObject. The updated network object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$NetworkId,

        [Parameter(Mandatory)]
        [string[]]$LocalDomains,

        [switch]$Append,

        [hashtable]$Session
    )

    $Endpoint = Join-Url '/v1/networks' $NetworkId

    if ($PSCmdlet.ShouldProcess("Network $NetworkId", 'Set local domains')) {
        if ($Append) {
            $Existing = Get-DNSFilterNetwork -ID $NetworkId -Session $Session
            $LocalDomains = @($LocalDomains) + @($Existing.attributes.local_domains) | Sort-Object -Unique
        }

        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            network = @{
                local_domains = $LocalDomains
            }
        } -Session $Session
    }
}
#EndRegion './Public/Network/Set-DNSFilterLocalDomain.ps1' 60
#Region './Public/Network/Set-DNSFilterLocalResolver.ps1' -1

function Set-DNSFilterLocalResolver {
    <#
    .SYNOPSIS
        Sets local resolvers for a DNSFilter network.

    .DESCRIPTION
        Replaces or appends local resolvers on a network. Use -Append to add to the
        existing list instead of replacing it.

    .PARAMETER NetworkId
        The network ID to configure.

    .PARAMETER LocalResolvers
        Array of local resolver addresses to set.

    .PARAMETER Append
        Append to existing resolvers instead of replacing them.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Set-DNSFilterLocalResolver -NetworkId 123 -LocalResolvers '192.168.1.1', '192.168.1.2'

    .EXAMPLE
        Set-DNSFilterLocalResolver -NetworkId 123 -LocalResolvers '10.0.0.1' -Append

    .OUTPUTS
        PSCustomObject. The updated network object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$NetworkId,

        [Parameter(Mandatory)]
        [string[]]$LocalResolvers,

        [switch]$Append,

        [hashtable]$Session
    )

    $Endpoint = Join-Url '/v1/networks' $NetworkId

    if ($PSCmdlet.ShouldProcess("Network $NetworkId", 'Set local resolvers')) {
        if ($Append) {
            $Existing = Get-DNSFilterNetwork -ID $NetworkId -Session $Session
            $LocalResolvers = @($LocalResolvers) + @($Existing.attributes.local_resolvers) | Sort-Object -Unique
        }

        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            network = @{
                local_resolvers = $LocalResolvers
            }
        } -Session $Session
    }
}
#EndRegion './Public/Network/Set-DNSFilterLocalResolver.ps1' 60
#Region './Public/Network/Update-DNSFilterNetwork.ps1' -1

function Update-DNSFilterNetwork {
    <#
    .SYNOPSIS
        Updates an existing DNSFilter network.

    .PARAMETER ID
        The ID of the network to update.

    .PARAMETER Name
        Updated network name.

    .PARAMETER OrganizationId
        Updated organization ID.

    .PARAMETER PolicyId
        Updated policy ID.

    .PARAMETER ScheduledPolicyId
        Updated scheduled policy ID.

    .PARAMETER BlockPageId
        Updated block page ID.

    .EXAMPLE
        Update-DNSFilterNetwork -ID 123 -Name 'Updated Office' -PolicyId 789

    .OUTPUTS
        PSCustomObject. The updated network object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [string]$Name,
        [int]$OrganizationId,
        [int]$PolicyId,
        [int]$ScheduledPolicyId,
        [int]$BlockPageId,

        [hashtable]$Session
    )

    $ParamMap = @{
        Name              = 'name'
        OrganizationId    = 'organization_id'
        PolicyId          = 'policy_id'
        ScheduledPolicyId = 'scheduled_policy_id'
        BlockPageId       = 'block_page_id'
    }

    $Network = @{}
    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Network[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    $Endpoint = Join-Url '/v1/networks' $ID

    if ($PSCmdlet.ShouldProcess("Network $ID", 'Update network')) {
        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            network = $Network
        } -Session $Session
    }
}
#EndRegion './Public/Network/Update-DNSFilterNetwork.ps1' 68
#Region './Public/Organization/Get-DNSFilterOrganization.ps1' -1

function Get-DNSFilterOrganization {
    <#
    .SYNOPSIS
        Retrieves DNSFilter organizations.

    .DESCRIPTION
        Returns organizations for the authenticated user, all organizations, or a specific organization by ID.

    .PARAMETER ID
        The ID of a specific organization to retrieve.

    .PARAMETER All
        Return all organizations.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterOrganization

    .EXAMPLE
        Get-DNSFilterOrganization -ID 123

    .EXAMPLE
        Get-DNSFilterOrganization -All

    .OUTPUTS
        PSCustomObject. Organization objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [hashtable]$Session
    )

    $Endpoint = '/v1/organizations'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    Invoke-DNSFilterRequest -Endpoint $Endpoint -Session $Session
}
#EndRegion './Public/Organization/Get-DNSFilterOrganization.ps1' 48
#Region './Public/Organization/New-DNSFilterOrganization.ps1' -1

function New-DNSFilterOrganization {
    <#
    .SYNOPSIS
        Creates a new DNSFilter organization.

    .PARAMETER Name
        The name of the organization.

    .PARAMETER BillingContactName
        Billing contact name.

    .PARAMETER BillingContactPhone
        Billing contact phone number.

    .PARAMETER BillingContactEmail
        Billing contact email address.

    .PARAMETER Address
        Organization address.

    .PARAMETER ManagedByMSPId
        The MSP ID that manages this organization.

    .PARAMETER UniqueId
        A unique identifier for the organization.

    .PARAMETER ExternalId
        An external identifier for the organization.

    .EXAMPLE
        New-DNSFilterOrganization -Name 'Contoso Ltd'

    .EXAMPLE
        New-DNSFilterOrganization -Name 'Contoso Ltd' -ManagedByMSPId 456 -ExternalId 'CRM-123'

    .OUTPUTS
        PSCustomObject. The created organization object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [string]$BillingContactName,
        [string]$BillingContactPhone,
        [string]$BillingContactEmail,
        [string]$Address,
        [int]$ManagedByMSPId,
        [string]$UniqueId,
        [string]$ExternalId,

        [hashtable]$Session
    )

    $ParamMap = @{
        Name                = 'name'
        BillingContactName  = 'billing_contact_name'
        BillingContactPhone = 'billing_contact_phone'
        BillingContactEmail = 'billing_contact_email'
        Address             = 'address'
        ManagedByMSPId      = 'managed_by_msp_id'
        UniqueId            = 'unique_id'
        ExternalId          = 'external_id'
    }

    $Org = @{}
    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Org[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    if ($PSCmdlet.ShouldProcess($Name, 'Create organization')) {
        Invoke-DNSFilterRequest -Endpoint '/v1/organizations' -Method Post -Body @{
            organization = $Org
        } -Session $Session
    }
}
#EndRegion './Public/Organization/New-DNSFilterOrganization.ps1' 81
#Region './Public/Organization/Remove-DNSFilterOrganization.ps1' -1

function Remove-DNSFilterOrganization {
    <#
    .SYNOPSIS
        Deletes a DNSFilter organization (MSP customer).

    .PARAMETER ID
        The ID of the organization to delete.

    .EXAMPLE
        Remove-DNSFilterOrganization -ID 123

    .OUTPUTS
        PSCustomObject. Deletion result.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    if ($PSCmdlet.ShouldProcess("Organization $ID", 'Delete organization')) {
        Invoke-DNSFilterRequest -Endpoint (Join-Url '/v1/organizations' $ID) -Method Delete -Session $Session
    }
}
#EndRegion './Public/Organization/Remove-DNSFilterOrganization.ps1' 28
#Region './Public/Organization/Update-DNSFilterOrganization.ps1' -1

function Update-DNSFilterOrganization {
    <#
    .SYNOPSIS
        Updates an existing DNSFilter organization.

    .PARAMETER ID
        The ID of the organization to update.

    .PARAMETER Name
        New name for the organization.

    .PARAMETER BillingContactName
        Updated billing contact name.

    .PARAMETER BillingContactPhone
        Updated billing contact phone number.

    .PARAMETER BillingContactEmail
        Updated billing contact email address.

    .PARAMETER Address
        Updated organization address.

    .PARAMETER ManagedByMSPId
        Updated MSP ID that manages this organization.

    .PARAMETER UniqueId
        Updated unique identifier.

    .PARAMETER ExternalId
        Updated external identifier.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Update-DNSFilterOrganization -ID 123 -Name 'Contoso LLC'

    .OUTPUTS
        PSCustomObject. The updated organization object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [string]$Name,
        [string]$BillingContactName,
        [string]$BillingContactPhone,
        [string]$BillingContactEmail,
        [string]$Address,
        [int]$ManagedByMSPId,
        [string]$UniqueId,
        [string]$ExternalId,

        [hashtable]$Session
    )

    $ParamMap = @{
        Name                = 'name'
        BillingContactName  = 'billing_contact_name'
        BillingContactPhone = 'billing_contact_phone'
        BillingContactEmail = 'billing_contact_email'
        Address             = 'address'
        ManagedByMSPId      = 'managed_by_msp_id'
        UniqueId            = 'unique_id'
        ExternalId          = 'external_id'
    }

    $Org = @{}
    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Org[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    $Endpoint = Join-Url '/v1/organizations' $ID

    if ($PSCmdlet.ShouldProcess("Organization $ID", 'Update organization')) {
        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            organization = $Org
        } -Session $Session
    }
}
#EndRegion './Public/Organization/Update-DNSFilterOrganization.ps1' 86
#Region './Public/Policy/Get-DNSFilterPolicy.ps1' -1

function Get-DNSFilterPolicy {
    <#
    .SYNOPSIS
        Retrieves DNSFilter policies.

    .DESCRIPTION
        Returns policies for the authenticated user, all policies, or a specific policy by ID.
        Use -IncludeGlobal to include global policies in results.

    .PARAMETER ID
        The ID of a specific policy to retrieve.

    .PARAMETER All
        Return all policies.

    .PARAMETER IncludeGlobal
        Include global policies in results.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterPolicy

    .EXAMPLE
        Get-DNSFilterPolicy -ID 123

    .EXAMPLE
        Get-DNSFilterPolicy -IncludeGlobal

    .OUTPUTS
        PSCustomObject. Policy objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [switch]$IncludeGlobal,

        [hashtable]$Session
    )

    $Endpoint = '/v1/policies'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    $Query = @{}
    if ($IncludeGlobal) { $Query.include_global_policies = 'true' }

    Invoke-DNSFilterRequest -Endpoint $Endpoint -QueryParameters $Query -Session $Session
}
#EndRegion './Public/Policy/Get-DNSFilterPolicy.ps1' 57
#Region './Public/Policy/New-DNSFilterPolicy.ps1' -1

function New-DNSFilterPolicy {
    <#
    .SYNOPSIS
        Creates a new DNSFilter policy.

    .PARAMETER Name
        The name of the policy.

    .PARAMETER OrganizationId
        The organization ID this policy belongs to.

    .PARAMETER AllowUnknownDomains
        Whether to allow unknown/uncategorized domains.

    .PARAMETER GoogleSafeSearch
        Enable Google SafeSearch enforcement.

    .PARAMETER BingSafeSearch
        Enable Bing SafeSearch enforcement.

    .PARAMETER DuckDuckGoSafeSearch
        Enable DuckDuckGo SafeSearch enforcement.

    .PARAMETER YouTubeRestricted
        Enable YouTube restricted mode.

    .PARAMETER YouTubeRestrictedLevel
        YouTube restriction level (e.g., 'moderate', 'strict').

    .PARAMETER Interstitial
        Enable interstitial warning pages.

    .PARAMETER PolicyIpId
        The policy IP ID.

    .PARAMETER WhitelistDomains
        Array of domains to allow.

    .PARAMETER BlacklistDomains
        Array of domains to block.

    .PARAMETER BlacklistCategories
        Array of category IDs to block.

    .PARAMETER IsGlobalPolicy
        Whether this is a global policy.

    .PARAMETER AllowApplications
        Array of application names to allow.

    .PARAMETER BlockApplications
        Array of application names to block.

    .EXAMPLE
        New-DNSFilterPolicy -Name 'Standard Policy' -OrganizationId 123

    .EXAMPLE
        New-DNSFilterPolicy -Name 'Strict Policy' -OrganizationId 123 -GoogleSafeSearch $true -YouTubeRestricted $true

    .OUTPUTS
        PSCustomObject. The created policy object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [int]$OrganizationId,

        [bool]$AllowUnknownDomains,
        [bool]$GoogleSafeSearch,
        [bool]$BingSafeSearch,
        [bool]$DuckDuckGoSafeSearch,
        [bool]$YouTubeRestricted,
        [string]$YouTubeRestrictedLevel,
        [bool]$Interstitial,
        [int]$PolicyIpId,
        [string[]]$WhitelistDomains,
        [string[]]$BlacklistDomains,
        [int[]]$BlacklistCategories,
        [bool]$IsGlobalPolicy,
        [string[]]$AllowApplications,
        [string[]]$BlockApplications,

        [hashtable]$Session
    )

    $Policy = @{
        name            = $Name
        organization_id = $OrganizationId
    }

    $ParamMap = @{
        AllowUnknownDomains    = 'allow_unknown_domains'
        GoogleSafeSearch       = 'google_safesearch'
        BingSafeSearch         = 'bing_safe_search'
        DuckDuckGoSafeSearch   = 'duck_duck_go_safe_search'
        YouTubeRestricted      = 'youtube_restricted'
        YouTubeRestrictedLevel = 'youtube_restricted_level'
        Interstitial           = 'interstitial'
        PolicyIpId             = 'policy_ip_id'
        WhitelistDomains       = 'whitelist_domains'
        BlacklistDomains       = 'blacklist_domains'
        BlacklistCategories    = 'blacklist_categories'
        IsGlobalPolicy         = 'is_global_policy'
        AllowApplications      = 'allow_applications'
        BlockApplications      = 'block_applications'
    }

    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Policy[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    if ($PSCmdlet.ShouldProcess($Name, 'Create policy')) {
        Invoke-DNSFilterRequest -Endpoint '/v1/policies' -Method Post -Body @{
            policy = $Policy
        } -Session $Session
    }
}
#EndRegion './Public/Policy/New-DNSFilterPolicy.ps1' 125
#Region './Public/Policy/Remove-DNSFilterPolicy.ps1' -1

function Remove-DNSFilterPolicy {
    <#
    .SYNOPSIS
        Deletes a DNSFilter policy.

    .PARAMETER ID
        The ID of the policy to delete.

    .EXAMPLE
        Remove-DNSFilterPolicy -ID 123

    .OUTPUTS
        PSCustomObject. Deletion result.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    if ($PSCmdlet.ShouldProcess("Policy $ID", 'Delete policy')) {
        Invoke-DNSFilterRequest -Endpoint (Join-Url '/v1/policies' $ID) -Method Delete -Session $Session
    }
}
#EndRegion './Public/Policy/Remove-DNSFilterPolicy.ps1' 28
#Region './Public/Policy/Update-DNSFilterPolicy.ps1' -1

function Update-DNSFilterPolicy {
    <#
    .SYNOPSIS
        Updates an existing DNSFilter policy.

    .PARAMETER ID
        The ID of the policy to update.

    .PARAMETER Name
        Updated policy name.

    .PARAMETER AllowUnknownDomains
        Whether to allow unknown/uncategorized domains.

    .PARAMETER GoogleSafeSearch
        Enable Google SafeSearch enforcement.

    .PARAMETER BingSafeSearch
        Enable Bing SafeSearch enforcement.

    .PARAMETER DuckDuckGoSafeSearch
        Enable DuckDuckGo SafeSearch enforcement.

    .PARAMETER YouTubeRestricted
        Enable YouTube restricted mode.

    .PARAMETER YouTubeRestrictedLevel
        YouTube restriction level.

    .PARAMETER Interstitial
        Enable interstitial warning pages.

    .PARAMETER PolicyIpId
        The policy IP ID.

    .PARAMETER WhitelistDomains
        Array of domains to allow.

    .PARAMETER BlacklistDomains
        Array of domains to block.

    .PARAMETER BlacklistCategories
        Array of category IDs to block.

    .PARAMETER AllowApplications
        Array of application names to allow.

    .PARAMETER BlockApplications
        Array of application names to block.

    .EXAMPLE
        Update-DNSFilterPolicy -ID 123 -Name 'Updated Policy'

    .EXAMPLE
        Update-DNSFilterPolicy -ID 123 -GoogleSafeSearch $true -YouTubeRestricted $true

    .OUTPUTS
        PSCustomObject. The updated policy object.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [string]$Name,
        [bool]$AllowUnknownDomains,
        [bool]$GoogleSafeSearch,
        [bool]$BingSafeSearch,
        [bool]$DuckDuckGoSafeSearch,
        [bool]$YouTubeRestricted,
        [string]$YouTubeRestrictedLevel,
        [bool]$Interstitial,
        [int]$PolicyIpId,
        [string[]]$WhitelistDomains,
        [string[]]$BlacklistDomains,
        [int[]]$BlacklistCategories,
        [string[]]$AllowApplications,
        [string[]]$BlockApplications,

        [hashtable]$Session
    )

    $Policy = @{}

    $ParamMap = @{
        Name                   = 'name'
        AllowUnknownDomains    = 'allow_unknown_domains'
        GoogleSafeSearch       = 'google_safesearch'
        BingSafeSearch         = 'bing_safe_search'
        DuckDuckGoSafeSearch   = 'duck_duck_go_safe_search'
        YouTubeRestricted      = 'youtube_restricted'
        YouTubeRestrictedLevel = 'youtube_restricted_level'
        Interstitial           = 'interstitial'
        PolicyIpId             = 'policy_ip_id'
        WhitelistDomains       = 'whitelist_domains'
        BlacklistDomains       = 'blacklist_domains'
        BlacklistCategories    = 'blacklist_categories'
        AllowApplications      = 'allow_applications'
        BlockApplications      = 'block_applications'
    }

    foreach ($Param in $ParamMap.GetEnumerator()) {
        if ($PSBoundParameters.ContainsKey($Param.Key)) {
            $Policy[$Param.Value] = $PSBoundParameters[$Param.Key]
        }
    }

    $Endpoint = Join-Url '/v1/policies' $ID

    if ($PSCmdlet.ShouldProcess("Policy $ID", 'Update policy')) {
        Invoke-DNSFilterRequest -Endpoint $Endpoint -Method Patch -Body @{
            policy = $Policy
        } -Session $Session
    }
}
#EndRegion './Public/Policy/Update-DNSFilterPolicy.ps1' 117
#Region './Public/UserAgent/Get-DNSFilterUserAgent.ps1' -1

function Get-DNSFilterUserAgent {
    <#
    .SYNOPSIS
        Retrieves DNSFilter user agents.

    .PARAMETER ID
        The ID of a specific user agent to retrieve.

    .PARAMETER All
        Return all user agents.

    .PARAMETER OrganizationId
        Filter user agents by organization ID.

    .PARAMETER Session
        Optional session object from Connect-DNSFilter for thread-safe usage.

    .EXAMPLE
        Get-DNSFilterUserAgent

    .EXAMPLE
        Get-DNSFilterUserAgent -ID 123

    .EXAMPLE
        Get-DNSFilterUserAgent -OrganizationId 456

    .OUTPUTS
        PSCustomObject. User agent objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ID')]
        [int]$ID,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [int]$OrganizationId,

        [hashtable]$Session
    )

    $Endpoint = '/v1/user_agents'
    if ($All) { $Endpoint = Join-Url $Endpoint 'all' }
    if ($PSBoundParameters.ContainsKey('ID')) { $Endpoint = Join-Url $Endpoint $ID }

    $Query = @{}
    if ($PSBoundParameters.ContainsKey('OrganizationId')) { $Query.organization_ids = $OrganizationId }

    Invoke-DNSFilterRequest -Endpoint $Endpoint -QueryParameters $Query -Session $Session
}
#EndRegion './Public/UserAgent/Get-DNSFilterUserAgent.ps1' 53
#Region './Public/UserAgent/Remove-DNSFilterUserAgent.ps1' -1

function Remove-DNSFilterUserAgent {
    <#
    .SYNOPSIS
        Removes a DNSFilter user agent.

    .PARAMETER ID
        The ID of the user agent to remove.

    .EXAMPLE
        Remove-DNSFilterUserAgent -ID 123

    .EXAMPLE
        Get-DNSFilterUserAgent -OrganizationId 456 | ForEach-Object { Remove-DNSFilterUserAgent -ID $_.id }

    .OUTPUTS
        PSCustomObject. Deletion result.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int]$ID,

        [hashtable]$Session
    )

    if ($PSCmdlet.ShouldProcess("User Agent $ID", 'Remove user agent')) {
        Invoke-DNSFilterRequest -Endpoint (Join-Url '/v1/user_agents' $ID) -Method Delete -Session $Session
    }
}
#EndRegion './Public/UserAgent/Remove-DNSFilterUserAgent.ps1' 31
#Region './suffix.ps1' -1

# Code to run after functions are loaded
#EndRegion './suffix.ps1' 2