Plugins/Technitium.ps1

function Get-CurrentPluginType { 'dns-01' }

function Add-DnsTxt {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,Position=0)]
        [string]$RecordName,
        [Parameter(Mandatory,Position=1)]
        [string]$TxtValue,
        [Parameter(Mandatory,Position=2)]
        [securestring]$TechnitiumToken,
        [Parameter(Mandatory,Position=3)]
        [string]$TechnitiumServer,
        [Parameter(Position=4)]
        [ValidateSet('https','http')]
        [string]$TechnitiumProtocol = 'https',
        [Parameter(Position=5)]
        [int]$TechnitiumTTL = 3600,
        [switch]$TechnitiumIgnoreCert,
        [Parameter(ValueFromRemainingArguments)]
        $ExtraParams
    )

    $commonParams = @{
        BaseUri = "$($TechnitiumProtocol)://$($TechnitiumServer)/api"
        Token = [pscredential]::new('a',$TechnitiumToken).GetNetworkCredential().Password
    }

    try {
        # ignore cert validation for the duration of the call
        if ($TechnitiumIgnoreCert) { Set-TechnitiumCertIgnoreOn }

        $resp = Invoke-Technitium 'zones/records/get' @{domain=$RecordName} @commonParams
        $txtRecs = $resp.response.records | Where-Object { $_.type -eq "TXT" }

        if ($txtRecs -and $TxtValue -in $txtRecs.rData.text) {
            Write-Debug "Record $RecordName with value $TxtValue already exists. Nothing to do."
            return
        }

        Write-Verbose "Adding TXT record $RecordName with value $TxtValue"
        $body = @{
            domain = $RecordName
            type = "TXT"
            ttl = $TechnitiumTTL
            text = $TxtValue
        }
        $null = Invoke-Technitium 'zones/records/add' $body @commonParams

        Write-Verbose "Successfully added TXT record $RecordName"
    } catch {
        throw
    } finally {
        # return cert validation back to normal
        if ($TechnitiumIgnoreCert) { Set-TechnitiumCertIgnoreOff }
    }

    <#
    .SYNOPSIS
        Add a DNS TXT record to Technitium DNS Server.

    .DESCRIPTION
        Add a DNS TXT record to Technitium DNS Server using the HTTP(S) API.

    .PARAMETER RecordName
        The fully qualified name of the TXT record.

    .PARAMETER TxtValue
        The value of the TXT record.

    .PARAMETER TechnitiumToken
        The Technitium DNS Server API authentication token.

    .PARAMETER TechnitiumServer
        The Technitium DNS Server hostname/IP and port (e.g., 'dns.example.com:5380' or '192.168.1.100:5380').

    .PARAMETER TechnitiumProtocol
        The protocol to use for API calls. Valid values are 'https' (default) or 'http'. HTTPS is strongly recommended for production use.

    .PARAMETER TechnitiumTTL
        The TTL of the new TXT record in seconds (default 3600).

    .PARAMETER TechnitiumIgnoreCert
        If specified, SSL certificate errors will be ignored (not recommended for production use).

    .PARAMETER ExtraParams
        This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.

    .EXAMPLE
        $token = ConvertTo-SecureString 'your-api-token' -AsPlainText -Force
        Add-DnsTxt '_acme-challenge.example.com' 'txt-value' $token 'dns.example.com:5380'

        Adds a TXT record using HTTPS (default protocol).

    .EXAMPLE
        $token = ConvertTo-SecureString 'your-api-token' -AsPlainText -Force
        Add-DnsTxt '_acme-challenge.example.com' 'txt-value' $token '192.168.1.100:5380' -TechnitiumProtocol 'http'

        Adds a TXT record using HTTP for troubleshooting.
    #>

}

function Remove-DnsTxt {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,Position=0)]
        [string]$RecordName,
        [Parameter(Mandatory,Position=1)]
        [string]$TxtValue,
        [Parameter(Mandatory,Position=2)]
        [securestring]$TechnitiumToken,
        [Parameter(Mandatory,Position=3)]
        [string]$TechnitiumServer,
        [Parameter(Position=4)]
        [ValidateSet('https','http')]
        [string]$TechnitiumProtocol = 'https',
        [Parameter(Position=5)]
        [int]$TechnitiumTTL = 3600,
        [switch]$TechnitiumIgnoreCert,
        [Parameter(ValueFromRemainingArguments)]
        $ExtraParams
    )

    $commonParams = @{
        BaseUri = "$($TechnitiumProtocol)://$($TechnitiumServer)/api"
        Token = [pscredential]::new('a',$TechnitiumToken).GetNetworkCredential().Password
    }

    try {
        # ignore cert validation for the duration of the call
        if ($TechnitiumIgnoreCert) { Set-TechnitiumCertIgnoreOn }

        $resp = Invoke-Technitium 'zones/records/get' @{domain=$RecordName} @commonParams
        $txtRecs = $resp.response.records | Where-Object { $_.type -eq "TXT" }

        if (-not $txtRecs -or $TxtValue -notin $txtRecs.rData.text) {
            Write-Debug "Record $RecordName with value $TxtValue doesn't exist. Nothing to do."
            return
        }

        Write-Verbose "Removing TXT record $RecordName with value $TxtValue"
        $body = @{
            domain = $RecordName
            type = "TXT"
            text = $TxtValue
        }
        $null = Invoke-Technitium 'zones/records/delete' $body @commonParams

        Write-Verbose "Successfully removed TXT record $RecordName"
    } catch {
        throw
    } finally {
        # return cert validation back to normal
        if ($TechnitiumIgnoreCert) { Set-TechnitiumCertIgnoreOff }
    }

    <#
    .SYNOPSIS
        Remove a DNS TXT record from Technitium DNS Server.

    .DESCRIPTION
        Remove a DNS TXT record from Technitium DNS Server using the HTTP(S) API.

    .PARAMETER RecordName
        The fully qualified name of the TXT record.

    .PARAMETER TxtValue
        The value of the TXT record.

    .PARAMETER TechnitiumToken
        The Technitium DNS Server API authentication token.

    .PARAMETER TechnitiumServer
        The Technitium DNS Server hostname/IP and port (e.g., 'dns.example.com:5380' or '192.168.1.100:5380').

    .PARAMETER TechnitiumProtocol
        The protocol to use for API calls. Valid values are 'https' (default) or 'http'. HTTPS is strongly recommended for production use.

    .PARAMETER TechnitiumTTL
        The TTL parameter (included for consistency but not used in delete operations).

    .PARAMETER TechnitiumIgnoreCert
        If specified, SSL certificate errors will be ignored (not recommended for production use).

    .PARAMETER ExtraParams
        This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.

    .EXAMPLE
        $token = ConvertTo-SecureString 'your-api-token' -AsPlainText -Force
        Remove-DnsTxt '_acme-challenge.example.com' 'txt-value' $token 'dns.example.com:5380'

        Removes a TXT record using HTTPS (default protocol).

    .EXAMPLE
        $token = ConvertTo-SecureString 'your-api-token' -AsPlainText -Force
        Remove-DnsTxt '_acme-challenge.example.com' 'txt-value' $token '192.168.1.100:5380' -TechnitiumProtocol 'http'

        Removes a TXT record using HTTP for troubleshooting.
    #>

}

function Save-DnsTxt {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromRemainingArguments)]
        $ExtraParams
    )

    <#
    .SYNOPSIS
        Not required.

    .DESCRIPTION
        This provider does not require calling this function to commit changes to DNS records.

    .PARAMETER ExtraParams
        This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
    #>

}

############################
# Helper Functions
############################

# API Docs:
# https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md

function Set-TechnitiumCertIgnoreOn {
    [CmdletBinding()]
    param()

    if ($script:SkipCertSupported) {
        # Core edition
        if (-not $script:UseBasic.SkipCertificateCheck) {
            Write-Debug "Disabling certificate validation for PS Core"
            # temporarily set skip to true
            $script:UseBasic.SkipCertificateCheck = $true
            # remember that we did
            $script:TechnitiumUnsetIgnoreAfter = $true
        }

    } else {
        Write-Debug "Disabling certificate validation for PS Desktop"
        # Desktop edition
        [CertValidation]::Ignore()
    }
}

function Set-TechnitiumCertIgnoreOff {
    [CmdletBinding()]
    param()

    if ($script:SkipCertSupported) {
        Write-Debug "Enabling certificate validation for PS Core"
        # Core edition
        if ($script:TechnitiumUnsetIgnoreAfter) {
            $script:UseBasic.SkipCertificateCheck = $false
            Remove-Variable TechnitiumUnsetIgnoreAfter -Scope Script
        }

    } else {
        # Desktop edition
        Write-Debug "Enabling certificate validation for PS Desktop"
        [CertValidation]::Restore()
    }
}

function Invoke-Technitium {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]$Endpoint,
        [Parameter(Mandatory, Position=1)]
        [hashtable]$Body,
        [Parameter(Mandatory, Position=2)]
        [string]$BaseUri,
        [Parameter(Mandatory, Position=3)]
        [string]$Token
    )

    $queryParams = @{
        Uri = "$BaseUri/$Endpoint"
        Method = 'Get'
        Body = $Body
        Verbose = $false
        ErrorAction = 'Stop'
    }
    # log the call without the token before we add it
    Write-Debug "GET $($queryParams.Uri)`n$($queryParams.Body | ConvertTo-Json)"
    # add the token
    $queryParams.Body.token = $Token

    try {
        $result = Invoke-RestMethod @queryParams @script:UseBasic
        if ($result.status -ne "ok") {
            throw "Technitium API returned status: $($result.status). Message: $($result.errorMessage)"
        }
        return $result
    } catch {
        throw
    }
}