Skip to content

Instantly share code, notes, and snippets.

@Skibisky
Last active June 5, 2025 05:21
Show Gist options
  • Save Skibisky/204642971da4c69cf5ebdaad828758b4 to your computer and use it in GitHub Desktop.
Save Skibisky/204642971da4c69cf5ebdaad828758b4 to your computer and use it in GitHub Desktop.
Win-Acme cPanel plugin script, expanded upon work by tmr-cf found at https://github.com/rmbolger/Posh-ACME/issues/376
<#
.SYNOPSIS
Add or remove a DNS TXT record to cPanel
.DESCRIPTION
Note that this script is intended to be run via the install script plugin from win-acme..
This script was copied and modified from the Posh-ACME repository Issue Tracker.
Please reference their license terms for use/modification: https://github.com/rmbolger/Posh-ACME/blob/main/LICENSE
Credit for the original script goes to tmr-cf, Thanks!
https://github.com/rmbolger/Posh-ACME/issues/376
This modified version supports non-internet facing subdomain hosts to validate with the external domain.
Eg. cPanel website is at https://example.wa.edu.au/, intranet host wants https://server.internal.example.wa.edu.au/
.PARAMETER RecordName
The fully qualified name of the TXT record.
.PARAMETER TxtValue
The value of the TXT record.
.PARAMETER cPanelDomain
The cPanel API Host.
.PARAMETER cPanelUserName
The cPanel API User Name.
.PARAMETER cPanelApiKey
The cPanel API Token.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
cPanel.ps1 create {RecordName} {Token} cPanelDomain cPanelUserName cPanelApiKey
cPanel.ps1 delete {RecordName} {Token} cPanelDomain cPanelUserName cPanelApiKey
.NOTES
Ideally put your api key in the secrets, and replace cPanelApiKey with {vault://json/cPanelToken}
#>
param(
[string]$Task,
[string]$RecordName,
[string]$TxtValue,
[string]$cPanelDomain,
[string]$cPanelUserName,
[string]$cPanelApiKey
)
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)]
[string]$cPanelDomain,
[Parameter(Mandatory, Position = 3)]
[string]$cPanelUsername,
[Parameter(Mandatory, Position = 4)]
[string]$cPanelAPIToken,
[Parameter(ValueFromRemainingArguments)]
$ExtraParams
)
# Do work here to add the TXT record. Remember to add @script:UseBasic
# to all calls to Invoke-RestMethod or Invoke-WebRequest.
# Find Zone serial number
if (-not ($ZoneSerial = Find-CPZoneSerial $RecordName $cPanelDomain $cPanelUsername $cPanelAPIToken)) {
throw "Unable to find matching zone for $RecordName."
}
$Header = @{"Authorization" = "cpanel " + $cPanelUsername + ":" + $cPanelAPIToken }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
# NOTE: cPanel will append `<sitename>.` to the end of dnames that don't end in `.`, so we [put one on the end
$JSON = [System.Web.HTTPUtility]::UrlEncode("{""dname"":""$RecordName."", ""ttl"":60, ""record_type"":""TXT"", ""data"":[""$TxtValue""]}")
$URI = "https://${cPanelDomain}:2083/execute/DNS/mass_edit_zone?zone=$cPanelDomain&serial=$ZoneSerial&add=$JSON"
Invoke-RestMethod -Method POST -headers $Header -uri $URI -DisableKeepAlive
<#
.SYNOPSIS
Add a DNS TXT record to <My DNS Server/Provider>
.DESCRIPTION
Description for <My DNS Server/Provider>
.PARAMETER RecordName
The fully qualified name of the TXT record.
.PARAMETER TxtValue
The value of the TXT record.
.PARAMETER cPanelDomain
The cPanel host.
.PARAMETER cPanelUsername
The which user to login.
.PARAMETER cPanelAPIToken
The API token to gain access.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
Add-DnsTxt '_acme-challenge.subsite.example.com' 'txt-value' 'example.com' 'username' 'mytokenvalue'
Adds a TXT record for the specified site with the specified value.
#>
}
function Remove-DnsTxt {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]$RecordName,
[Parameter(Mandatory, Position = 1)]
[string]$TxtValue,
[Parameter(Mandatory, Position = 2)]
[string]$cPanelDomain,
[Parameter(Mandatory, Position = 3)]
[string]$cPanelUsername,
[Parameter(Mandatory, Position = 4)]
[string]$cPanelAPIToken,
[Parameter(ValueFromRemainingArguments)]
$ExtraParams
)
# Do work here to remove the TXT record. Remember to add @script:UseBasic
# to all calls to Invoke-RestMethod or Invoke-WebRequest.
# Find Zone serial number
if (-not ($ZoneSerial = Find-CPZoneSerial $RecordName $cPanelDomain $cPanelUsername $cPanelAPIToken)) {
throw "Unable to find matching zone for $RecordName."
}
# Find Zone line number
if (-not ($ZoneLine = Find-CPZoneLine $RecordName $TxtValue $cPanelDomain $cPanelUsername $cPanelAPIToken)) {
throw "Unable to find matching line for $RecordName."
}
$Header = @{"Authorization" = "cpanel " + $cPanelUsername + ":" + $cPanelAPIToken }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
$URI = "https://${cPanelDomain}:2083/execute/DNS/mass_edit_zone?zone=$cPanelDomain&serial=$ZoneSerial&remove=$ZoneLine"
Invoke-RestMethod -Method POST -headers $Header -uri $URI -DisableKeepAlive
<#
.SYNOPSIS
Remove a DNS TXT record from <My DNS Server/Provider>
.DESCRIPTION
Description for <My DNS Server/Provider>
.PARAMETER RecordName
The fully qualified name of the TXT record.
.PARAMETER TxtValue
The value of the TXT record.
.PARAMETER cPanelDomain
The cPanel host.
.PARAMETER cPanelUsername
The which user to login.
.PARAMETER cPanelAPIToken
The API token to gain access.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
Remove-DnsTxt '_acme-challenge.example.com' 'txt-value'
Removes a TXT record for the specified site with the specified value.
#>
}
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
############################
# Add a commented link to API docs if they exist.
# Add additional functions here if necessary.
# Try to follow verb-noun naming guidelines.
# https://msdn.microsoft.com/en-us/library/ms714428
# https://hostname.example.com:2083/cpsess##########/execute/DNS/parse_zone?zone=example.com
function Find-CPZoneSerial {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]$RecordName,
[Parameter(Mandatory, Position = 1)]
[string]$cPanelDomain,
[Parameter(Mandatory, Position = 2)]
[string]$cPanelUsername,
[Parameter(Mandatory, Position = 3)]
[string]$cPanelAPIToken
)
$Header = @{"Authorization" = "cpanel " + $cPanelUsername + ":" + $cPanelAPIToken }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
$URI = "https://${cPanelDomain}:2083/execute/DNS/parse_zone?zone=$cPanelDomain"
$Response = Invoke-RestMethod -Method POST -headers $Header -uri $URI -DisableKeepAlive
$SOARecord = $Response.data | Where-Object { ($_."record_type" -eq 'SOA') }
$SOARecordData = $SOARecord.data_b64
$Array = $SOARecordData.Split("`r`n")
return [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($Array[2]))
}
function Find-CPZoneLine {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]$RecordName,
[Parameter(Mandatory, Position = 1)]
[string]$TxtValue,
[Parameter(Mandatory, Position = 2)]
[string]$cPanelDomain,
[Parameter(Mandatory, Position = 3)]
[string]$cPanelUsername,
[Parameter(Mandatory, Position = 4)]
[string]$cPanelAPIToken
)
$Header = @{"Authorization" = "cpanel " + $cPanelUsername + ":" + $cPanelAPIToken }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
$URI = "https://${cPanelDomain}:2083/execute/DNS/parse_zone?zone=$cPanelDomain"
$Response = Invoke-RestMethod -Method POST -headers $Header -uri $URI -DisableKeepAlive
# cPanel will put `.<site>.` on the end of dnames that don't end in `.`
$RecordNameb64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($RecordName + "."))
$TxtValueb64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($TxtValue))
$Record = $Response.data | Where-Object { ($_."record_type" -eq 'TXT') -and ($_."dname_b64" -eq $RecordNameb64) -and ($_."data_b64" -eq $TxtValueb64) }
return $Record.line_index
}
if ($Task -eq 'create') {
Add-DnsTxt $RecordName $TxtValue $cPanelDomain $cPanelUserName $cPanelApiKey
}
if ($Task -eq 'delete') {
Remove-DnsTxt $RecordName $TxtValue $cPanelDomain $cPanelUserName $cPanelApiKey
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment