Plugins/TencentDNS.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)] [string]$TencentKeyId, [Parameter(Mandatory, Position = 3)] [securestring]$TencentSecret, [Parameter(ValueFromRemainingArguments)] $ExtraParams ) # find the zone for this record try { $zoneName = Find-TencentZone $RecordName $TencentKeyId $TencentSecret } catch { throw } Write-Debug "Found zone $zoneName" # grab the relative portion of the fqdn $recShort = ($RecordName -ireplace [regex]::Escape($zoneName), [string]::Empty).TrimEnd('.') if ($recShort -eq $RecordName) { $recShort = '@' } # query for an existing record try { $record = Find-TencentRecord -Domain $zoneName -Subdomain $recShort -RecordType 'TXT' -RecordValue $TxtValue -TencentKeyId $TencentKeyId -TencentSecret $TencentSecret } catch { throw } if ($null -eq $record) { # add the record Write-Verbose "Adding a TXT record for $RecordName with value $TxtValue" $body = @{ SubDomain = $recShort Domain = $zoneName RecordType = 'TXT' Value = $TxtValue RecordLine = '默认' # default } $response = Invoke-TencentRest CreateRecord $body $TencentKeyId $TencentSecret Invoke-Response $response } else { Write-Debug "Record $RecordName already contains $TxtValue. Nothing to do." } <# .SYNOPSIS Add a DNS TXT record to Tencentyun (Tencent Cloud) .DESCRIPTION Add a DNS TXT record to Tencentyun (Tencent Cloud) .PARAMETER RecordName The fully qualified name of the TXT record. .PARAMETER TxtValue The value of the TXT record. .PARAMETER TencentKeyId The Access Key ID for your Tencentyun account. .PARAMETER TencentSecret The Access Secret for your Tencentyun account. .PARAMETER ExtraParams This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports. .EXAMPLE $secret = Read-Host "Secret" -AsSecureString PS C:\>Add-DnsTxt '_acme-challenge.example.com' 'txt-value' 'key-id' $secret Adds a TXT record using a securestring object for TencentSecret. #> } function Remove-DnsTxt { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$RecordName, [Parameter(Mandatory, Position = 1)] [string]$TxtValue, [Parameter(Mandatory, Position = 2)] [string]$TencentKeyId, [Parameter(Mandatory, Position = 3)] [securestring]$TencentSecret, [Parameter(ValueFromRemainingArguments)] $ExtraParams ) # find the zone for this record try { $zoneName = Find-TencentZone $RecordName $TencentKeyId $TencentSecret } catch { throw } Write-Debug "Found zone $zoneName" # grab the relative portion of the fqdn $recShort = ($RecordName -ireplace [regex]::Escape($zoneName), [string]::Empty).TrimEnd('.') if ($recShort -eq $RecordName) { $recShort = '@' } # query for an existing record try { $record = Find-TencentRecord -Domain $zoneName -Subdomain $recShort -RecordType 'TXT' -RecordValue $TxtValue -TencentKeyId $TencentKeyId -TencentSecret $TencentSecret } catch { throw } Write-Debug "Found Record $record" if ($null -eq $record) { Write-Debug "Record $RecordName with value $TxtValue doesn't exist. Nothing to do." } else { # remove the record Write-Verbose "Removing TXT record for $RecordName with RecordId $($record.RecordId)" $body = @{ Domain = $zoneName RecordId = $record.RecordId } $response = Invoke-TencentRest DeleteRecord $body $TencentKeyId $TencentSecret Invoke-Response $response } <# .SYNOPSIS Remove a DNS TXT record from Tencentyun (Tencent Cloud) .DESCRIPTION Remove a DNS TXT record from Tencentyun (Tencent Cloud) .PARAMETER RecordName The fully qualified name of the TXT record. .PARAMETER TxtValue The value of the TXT record. .PARAMETER TencentKeyId The Access Key ID for your Tencentyun account. .PARAMETER TencentSecret The Access Secret for your Tencentyun account. .PARAMETER ExtraParams This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports. .EXAMPLE $secret = Read-Host "Secret" -AsSecureString PS C:\>Remove-DnsTxt '_acme-challenge.example.com' 'txt-value' 'key-id' $secret Removes a TXT record using a securestring object for TencentSecret. #> } 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://www.tencentcloud.com/document/product/228/31723 # https://cloud.tencent.com/document/api/1427 function Invoke-TencentRest { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$action, [Parameter(Position = 1)] [Object]$bodyData, [Parameter(Mandatory, Position = 2)] [string]$secretId, [Parameter(Mandatory, Position = 3)] [securestring]$AccessSecret ) $secretKey = [pscredential]::new('a', $AccessSecret).GetNetworkCredential().Password $body = $bodyData | ConvertTo-Json -Compress # BuildRequest $region = "" $token = "" $version = "2021-03-23" $apihost = "dnspod.tencentcloudapi.com" $contentType = "application/json;charset=utf-8" $epochStart = Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0 $timestamp = [Math]::Round((Get-Date).ToUniversalTime().Subtract($epochStart).TotalSeconds) # Signature mechanism # https://cloud.tencent.com/document/api/1427/56189 $auth = GetAuth -secretId $secretId -secretKey $secretKey -apihost $apihost -contentType $contentType -timestamp $timestamp -body $body -action $action $queryParams = @{ Uri = "https://$apihost" Method = 'Post' Headers = @{ 'Authorization' = $auth 'User-Agent' = '' 'Host' = $apihost 'X-TC-Timestamp' = $timestamp 'X-TC-Version' = $version 'X-TC-Action' = $action 'X-TC-Region' = $region 'X-TC-Token' = $token } Body = $body ContentType = $contentType Verbose = $false ErrorAction = 'Stop' } Write-Debug "POST $($queryParams.Uri)`n$($queryParams.Body)" # PowerShell 7 does header validation by default now and rejects the request # unless you specify the SkipHeaderValidation flag. if ('SkipHeaderValidation' -in (Get-Command Invoke-RestMethod).Parameters.Keys) { $queryParams.SkipHeaderValidation = $true } Invoke-RestMethod @queryParams } function GetAuth { [CmdletBinding()] param( [string]$secretId, [string]$secretKey, [string]$apihost, [string]$contentType, [long]$timestamp, [string]$body, [string]$action ) $canonicalURI = "/" $xtcaction = $action.ToLower() $canonicalHeaders = "content-type:$contentType`nhost:$apihost`nx-tc-action:$xtcaction`n" $signedHeaders = "content-type;host;x-tc-action" $hashedRequestPayload = Sha256Hex $body $canonicalRequest = "POST`n$canonicalURI`n`n$canonicalHeaders`n$signedHeaders`n$hashedRequestPayload" Write-Debug "canonicalRequest $canonicalRequest" $algorithm = "TC3-HMAC-SHA256" $epochStart = Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0 $timestampDateTime = $epochStart.AddSeconds($timestamp) $date = $timestampDateTime.ToString("yyyy-MM-dd") $service = $apihost.Split(".")[0] $credentialScope = "$date/$service/tc3_request" $hashedCanonicalRequest = Sha256Hex $canonicalRequest $stringToSign = "$algorithm`n$timestamp`n$credentialScope`n$hashedCanonicalRequest" Write-Debug "stringToSign $stringToSign" $tc3SecretKey = [Text.Encoding]::UTF8.GetBytes("TC3" + $secretKey) $secretDate = HmacSha256 -key $tc3SecretKey -msg ([Text.Encoding]::UTF8.GetBytes($date)) $secretService = HmacSha256 -key $secretDate -msg ([Text.Encoding]::UTF8.GetBytes($service)) $secretSigning = HmacSha256 -key $secretService -msg ([Text.Encoding]::UTF8.GetBytes("tc3_request")) $signatureBytes = HmacSha256 -key $secretSigning -msg ([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = ($signatureBytes | ForEach-Object { $_.ToString("x2") }) -join '' $signature = $signature.ToLower() $auth = "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature" Write-Debug "auth $auth" return $auth } function Sha256Hex { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String]$inputString ) $sha256 = [Security.Cryptography.SHA256]::Create() $inputBytes = [Text.Encoding]::UTF8.GetBytes($inputString) $hashBytes = $sha256.ComputeHash($inputBytes) $hashHexString = [BitConverter]::ToString($hashBytes) -replace "-" return $hashHexString.ToLower() } function HmacSha256 { [CmdletBinding()] param( [byte[]]$key, [byte[]]$msg ) $mac = [Security.Cryptography.HMACSHA256]::new($key) return $mac.ComputeHash($msg) } function Invoke-Response { [CmdletBinding()] param( [object]$response ) if ($response.Response.Error) { $strRps = $response | ConvertTo-Json -Compress Write-Warning "Response Error $strRps" throw } } function Find-TencentZone { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$RecordName, [Parameter(Mandatory, Position = 1)] [string]$TencentKeyId, [Parameter(Mandatory, Position = 2)] [securestring]$TencentSecret ) # setup a module variable to cache the record to zone ID mapping # so it's quicker to find later if (!$script:TencentRecordZones) { $script:TencentRecordZones = @{} } # check for the record in the cache if ($script:TencentRecordZones.ContainsKey($RecordName)) { return $script:TencentRecordZones.$RecordName } # Search for the zone from longest to shortest set of FQDN pieces. $pieces = $RecordName.Split('.') for ($i = 0; $i -lt ($pieces.Count - 1); $i++) { $zoneTest = $pieces[$i..($pieces.Count - 1)] -join '.' Write-Debug "Checking $zoneTest" try { $body = @{ Keyword = $zoneTest } $response = Invoke-TencentRest DescribeDomainList $body $TencentKeyId $TencentSecret # check for results if ($response.Response.DomainCountInfo.DomainTotal -gt 0) { $script:TencentRecordZones.$RecordName = $response.Response.DomainList[0].Name # or PunyCode? return $script:TencentRecordZones.$RecordName } } catch { throw } } throw "No zone found for $RecordName" } function Find-TencentRecord { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Domain, [Parameter(Mandatory, Position = 1)] [AllowEmptyString()] [string]$Subdomain, [Parameter(Mandatory, Position = 2)] [string]$RecordType, [Parameter(Position = 3)] [string]$RecordValue, [Parameter(Mandatory, Position = 4)] [string]$TencentKeyId, [Parameter(Mandatory, Position = 5)] [securestring]$TencentSecret ) try { $body = @{ Domain = $Domain Subdomain = $Subdomain #Keyword =$TxtValue # can not add this param RecordType = $RecordType } $response = Invoke-TencentRest DescribeRecordList $body $TencentKeyId $TencentSecret } catch { throw } if ($response.Response.RecordCountInfo.TotalCount -gt 0) { $recordList = $response.Response.RecordList if ($RecordValue -eq $null) { return $recordList[0] } # found data . then foreach for ($i = 0; $i -lt ($recordList.Count); $i++) { $item = $recordList[$i] if ($item.Value -eq $RecordValue) { #Write-Debug "Find RecordItem $item" return $item } } } Write-Debug "No Record found for $Domain Subdomain $Subdomain RecordType $RecordType RecordValue $RecordValue" } |