Plugins/WEDOS.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)] [pscredential]$WedosCredential, [Parameter(ValueFromRemainingArguments)] $ExtraParams ) $zone = Find-Zone $RecordName $WedosCredential if (-not $zone) { throw "Unable to find zone for $RecordName in WEDOS" } Write-Debug "Found zone $zone" # setup a tracking variable for zones we need to "commit" if (-not $script:WedosZonesToSave) { $script:WedosZonesToSave = @() } if (-not (Find-TxtRec $RecordName $TxtValue $zone $WedosCredential)) { # empty string short names are ok for zone apex $recShort = ($RecordName -ireplace [regex]::Escape($zone), [string]::Empty).TrimEnd('.') $data = @{ domain = $zone name = $recShort type = 'TXT' rdata = $TxtValue ttl = 300 # minimum } Invoke-Wedos dns-row-add $WedosCredential -Data $data if ($zone -notin $script:WedosZonesToSave) { $script:WedosZonesToSave += $zone } } else { Write-Debug "Record $RecordName already contains $TxtValue. Nothing to do." } <# .SYNOPSIS Add a DNS TXT record to WEDOS .DESCRIPTION Add a DNS TXT record to WEDOS .PARAMETER RecordName The fully qualified name of the TXT record. .PARAMETER TxtValue The value of the TXT record. .PARAMETER WedosCredential The account username and API password. .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.example.com' 'txt-value' -WedosCredential (Get-Credential) 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)] [pscredential]$WedosCredential, [Parameter(ValueFromRemainingArguments)] $ExtraParams ) $zone = Find-Zone $RecordName $WedosCredential if (-not $zone) { throw "Unable to find zone for $RecordName in WEDOS" } Write-Debug "Found zone $zone" # setup a tracking variable for zones we need to "commit" if (-not $script:WedosZonesToSave) { $script:WedosZonesToSave = @() } if (-not ($rec = Find-TxtRec $RecordName $TxtValue $zone $WedosCredential)) { Write-Debug "Record $RecordName with value $TxtValue doesn't exist. Nothing to do." } else { $data = @{ domain = $zone row_id = $rec.ID } Invoke-Wedos dns-row-delete $WedosCredential -Data $data if ($zone -notin $script:WedosZonesToSave) { $script:WedosZonesToSave += $zone } } <# .SYNOPSIS Remove a DNS TXT record from WEDOS .DESCRIPTION Remove a DNS TXT record from WEDOS .PARAMETER RecordName The fully qualified name of the TXT record. .PARAMETER TxtValue The value of the TXT record. .PARAMETER WedosCredential The account username and API password. .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' -WedosCredential (Get-Credential) Removes a TXT record for the specified site with the specified value. #> } function Save-DnsTxt { [CmdletBinding()] param( [Parameter(Mandatory)] [pscredential]$WedosCredential, [Parameter(ValueFromRemainingArguments)] $ExtraParams ) foreach ($zone in $script:WedosZonesToSave) { Write-Verbose "Applying changes for $zone zone" $data = @{ name = $zone } Invoke-Wedos dns-domain-commit $WedosCredential -Data $data } $script:WedosZonesToSave = @() <# .SYNOPSIS Commit changes to WEDOS zones. .DESCRIPTION Commit changes to WEDOS zones. .PARAMETER WedosCredential The account username and API password. .PARAMETER ExtraParams This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports. .EXAMPLE Save-DnsTxt -WedosCredential (Get-Credential) Commits changes to zones modified by Add-DnsTxt and Save-DnsTxt #> } ############################ # Helper Functions ############################ # https://kb.wedos.com/cs/wapi-api-rozhrani/zakladni-informace-wapi-api-rozhrani/wapi-zakladni-informace/ # https://kb.wedos.com/en/wapi-api-interface/wdns-en/wapi-wdns/ function Invoke-Wedos { [CmdletBinding()] param( [Parameter(Mandatory,Position=0)] [string]$Command, [Parameter(Mandatory,Position=1)] [pscredential]$Credential, [hashtable]$Data, [int[]]$AltGoodCodes=@() ) # The auth protocol for this API is rather...unique. # Get a SHA1 hash of the password $sha1 = [Security.Cryptography.SHA1CryptoServiceProvider]::new() $hashBytes = $sha1.ComputeHash([Text.Encoding]::UTF8.GetBytes($Credential.GetNetworkCredential().Password)) $pHash = [BitConverter]::ToString($hashBytes).Replace('-','').ToLower() # For some reason, the auth protocol requires the current 00-24 hour # with leading zeros specifically in the Europe/Prague time zone. $nowUtc = [DateTime]::UtcNow $hour = [TimeZoneInfo]::ConvertTimeFromUtc( $nowUtc, # Despite being "Standard" time, this will auto-convert # to "Summer" time when appropriate. [TimeZoneInfo]::FindSystemTimeZoneById('Central Europe Standard Time') ).ToString('HH') # Concatenate the username, hashed password, and hour # and then SHA1 hash the whole thing $authRaw = '{0}{1}{2}' -f $Credential.Username,$pHash,$hour Write-Debug "authRaw = $authRaw" $hashBytes = $sha1.ComputeHash([Text.Encoding]::UTF8.GetBytes($authRaw)) $auth = [BitConverter]::ToString($hashBytes).Replace('-','').ToLower() # Build the request object $req = @{ request = @{ user = $Credential.UserName auth = $auth clTRID = "Posh-ACME $(New-Guid)" # client request ID command = $Command } } if ($Data) { $req.request.data = $Data } $queryParams = @{ Uri = 'https://api.wedos.com/wapi/json' Method = 'POST' # Send the JSON request as a value of "request" that is # application/x-www-form-urlencoded instead of just raw JSON Body = @{request=($req | ConvertTo-Json -Compress -Depth 10)} Verbose = $false ErrorAction = 'Stop' } Write-Debug "POST $($queryParams.Uri)`n$($req|ConvertTo-Json -Depth 10)" $resp = Invoke-RestMethod @queryParams @script:UseBasic Write-Debug "Response:`n$($resp|ConvertTo-Json -Depth 10)" if ($resp.response.code -ne 1000 -and $resp.response.code -notin $AltGoodCodes) { "WEDOS API Error $($resp.response.code): $($resp.response.result)" } return $resp.response.data } function Find-Zone { [CmdletBinding()] param( [Parameter(Mandatory,Position=0)] [string]$RecordName, [Parameter(Mandatory,Position=1)] [pscredential]$Credential ) # setup a module variable to cache the record to zone mapping # so it's quicker to find later and uses fewer API calls if (!$script:WedosRecordZones) { $script:WedosRecordZones = @{} } # check for the record in the cache if ($script:WedosRecordZones.ContainsKey($RecordName)) { return $script:WedosRecordZones.$RecordName } # Get all of the domains on the account $zones = Invoke-Wedos dns-domains-list $Credential | Select-Object -Expand domain if (-not $zones) { Write-Warning "No WEDOS hosted domains found." return } # find the zone for the closest/deepest sub-zone that would contain the record. $pieces = $RecordName.Split('.') for ($i=0; $i -lt ($pieces.Count-1); $i++) { $zoneTest = $pieces[$i..($pieces.Count-1)] -join '.' Write-Debug "Checking $zoneTest" $match = $zones | Where-Object { $zoneTest -eq $_.name } | Select-Object -First 1 if ($match) { $script:WedosRecordZones.$RecordName = $zoneTest return $zoneTest } } } function Find-TxtRec { [CmdletBinding()] param( [Parameter(Mandatory,Position=0)] [string]$RecordName, [Parameter(Mandatory,Position=1)] [string]$TxtValue, [Parameter(Mandatory,Position=2)] [string]$Zone, [Parameter(Mandatory,Position=3)] [pscredential]$Credential ) # Get all of the records in the zone $recs = Invoke-Wedos dns-rows-list $Credential -Data @{domain=$Zone} | Select-Object -Expand row if (-not $recs) { Write-Warning "No WEDOS records found." return } return $recs | Where-Object { $RecordName -eq "$($_.name).$Zone".Trim('.') -and $_.rdtype -eq 'TXT' -and $_.rdata -eq $TxtValue } } |