FTFLSTWD-Tools.psm1

# ftflstwd-tools.psm1
function Get-PublicIP {
    <#
    .SYNOPSIS
        Retrieves public IP address information using the IPinfo API.
    .DESCRIPTION
        Fetches IPv4 and IPv6 information for the current machine or specified IP addresses using IPinfo's v4/v6 Lite API.
    .PARAMETER IPAddress
        Optional IP address(es) (IPv4 or IPv6) to query. Accepts multiple IPs via pipeline or array. If omitted, retrieves public IPv4 and IPv6 information for the current machine.
    .EXAMPLE
        Get-PublicIP
        Retrieves public IPv4 and IPv6 information for the current machine.
    .EXAMPLE
        Get-PublicIP -IPAddress "8.8.8.8"
        Retrieves information for the specified IPv4 address.
    .EXAMPLE
        "8.8.8.8", "2001:4860:4860::8888" | Get-PublicIP
        Retrieves information for multiple IP addresses via pipeline.
    .NOTES
        Requires IPINFO_API_KEY environment variable to be set with a valid IPinfo API token.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[pscustomobject]])]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateScript({ $_ -eq '' -or [System.Net.IPAddress]::TryParse($_, [ref]$null) })]
        [AllowEmptyString()]
        [string[]]$IPAddress
    )

    begin {
        # Validate API token early
        $apiToken = $env:IPINFO_API_KEY
        if (-not $apiToken) {
            throw "IPINFO_API_KEY environment variable is not set."
        }

        # Define constants
        $baseUriV4 = "https://v4.api.ipinfo.io/lite"
        $baseUriV6 = "https://v6.api.ipinfo.io/lite"
        $apiTokenQuery = "?token=$apiToken"

        # Helper function for API requests
        function Invoke-ApiRequest {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [string]$Uri,
                [Parameter(Mandatory = $true)]
                [string]$Context
            )
            try {
                Write-Verbose "Querying API: $Uri"
                return Invoke-RestMethod -Uri $Uri -Method Get -ErrorAction Stop
            }
            catch {
                $errorMessage = "Failed to fetch $Context from $Uri`: $($_.Exception.Message)"
                if ($_.Exception.Response) {
                    $errorMessage += " (HTTP $($_.Exception.Response.StatusCode): $($_.Exception.Response.StatusDescription))"
                }
                Write-Error $errorMessage
                return $null
            }
        }

        # Initialize results collection
        $results = [System.Collections.Generic.List[pscustomobject]]::new()
    }

    process {
        # Handle no IP address provided (fetch "me" information)
        if (-not $IPAddress -or $IPAddress.Count -eq 0 -or ($IPAddress.Count -eq 1 -and [string]::IsNullOrEmpty($IPAddress[0]))) {
            Write-Verbose "No IP address provided. Fetching IPv4 and IPv6 information."
            $requests = @(
                @{ Uri = "$baseUriV4/me$apiTokenQuery"; Context = "IPv4 'me'" }
                @{ Uri = "$baseUriV6/me$apiTokenQuery"; Context = "IPv6 'me'" }
            )

            foreach ($req in $requests) {
                if ($result = Invoke-ApiRequest -Uri $req.Uri -Context $req.Context) {
                    $results.Add($result)
                }
            }
        }
        else {
            # Process each IP address
            foreach ($ip in $IPAddress) {
                if ([string]::IsNullOrEmpty($ip)) {
                    continue  # Skip empty strings in array
                }
                Write-Verbose "Querying specific IP: $ip"
                try {
                    $parsedIP = [System.Net.IPAddress]::Parse($ip)
                    $baseUri = switch ($parsedIP.AddressFamily) {
                        ([System.Net.Sockets.AddressFamily]::InterNetwork) { $baseUriV4 }
                        ([System.Net.Sockets.AddressFamily]::InterNetworkV6) { $baseUriV6 }
                        default { throw "Unsupported IP address family for '$ip'." }
                    }
                    $uri = "$baseUri/$ip$apiTokenQuery"
                    if ($result = Invoke-ApiRequest -Uri $uri -Context "IP '$ip'") {
                        $results.Add($result)
                    }
                }
                catch {
                    Write-Error "Invalid IP address: $ip. Error: $($_.Exception.Message)"
                }
            }
        }
    }

    end {
        return $results
    }
}

# Export the function
Export-ModuleMember -Function Get-PublicIP