CloudflareApi.psm1
#Region '.\Enum\DnsTypes.ps1' 0 enum DnsTypes { A # AAAA CNAME # HTTPS TXT SRV # LOC MX # NS # CERT # DNSKEY # DS # NAPTR # SMIMEA # SSHFP # SVCB # TLSA # URI } #EndRegion '.\Enum\DnsTypes.ps1' 22 #Region '.\Classes\CloudFlareRecord.ps1' 0 class CloudFlareRecord { [string]ToJson() { Return ($this.ToBody() | ConvertTo-Json -Depth 100 -Compress) } [object]GetPropertyNames() { $properties = (($this.psobject.Properties).Name) Return $properties } } #EndRegion '.\Classes\CloudFlareRecord.ps1' 14 #Region '.\Classes\CloudflareRecordA.ps1' 0 class CloudflareRecordA : CloudFlareRecord { # Properties [string]$Name [string]$Content [bool]$Proxied [int]$TTL # Methods CloudFlareRecordA ([string]$Name, [string]$Content, [bool]$Proxied, [int]$TTL) { $this.Name = $Name $this.Content = $Content $this.proxied = if ($Proxied) { $Proxied } else { $false } $this.ttl = if ($ttl) { $ttl } else { 1 } } CloudFlareRecordA () { $this.Name $this.Content $this.proxied $this.ttl = 1 } [Object]ToBody() { $objectData = [PSCustomObject][ordered]@{ type = "A" name = $this.name content = $this.Content proxied = $this.Proxied ttl = $this.TTL } return $objectData } } #EndRegion '.\Classes\CloudflareRecordA.ps1' 39 #Region '.\Classes\CloudflareRecordCNAME.ps1' 0 class CloudflareRecordCNAME : CloudFlareRecord { # Properties [string]$Name [string]$Content [bool]$Proxied [int]$TTL # Methods CloudFlareRecordCNAME ([string]$Name, [string]$Content, [bool]$Proxied, [int]$TTL) { $this.Name = $Name $this.Content = $Content $this.proxied = if ($Proxied) { $Proxied } else { $false } $this.ttl = if ($ttl) { $ttl } else { 1 } } CloudFlareRecordCNAME () { $this.Name $this.Content $this.proxied $this.ttl = 1 } [Object]ToBody() { $objectData = [PSCustomObject][ordered]@{ type = "CNAME" name = $this.name content = $this.Content proxied = $this.Proxied ttl = $this.TTL } return $objectData } } #EndRegion '.\Classes\CloudflareRecordCNAME.ps1' 39 #Region '.\Classes\CloudflareRecordMX.ps1' 0 class CloudflareRecordMX : CloudFlareRecord { # Properties [string]$Name [string]$Content [int]$Priority [int]$TTL # Methods CloudFlareRecordMX ([string]$Name, [string]$Content, [int]$Priority, [int]$TTL) { $this.Name = $Name $this.Content = $Content $this.Priority = $Priority $this.TTL = if ($ttl) { $ttl } else { 1 } } CloudFlareRecordMX () { $this.Name $this.Content $this.Priority $this.TTL = 1 } [Object]ToBody() { $objectData = [PSCustomObject][ordered]@{ type = "MX" name = $this.name content = $this.Content priority = $this.priority ttl = $this.TTL } return $objectData } } #EndRegion '.\Classes\CloudflareRecordMX.ps1' 38 #Region '.\Classes\CloudFlareRecordSRV.ps1' 0 class CloudFlareRecordSRV : CloudFlareRecord { # Properties [string]$Name [string]$Service [string]$Proto [int]$Priority [int]$Weight [int]$Port [string]$Target [int]$TTL # Methods CloudFlareRecordSRV ([string]$Name, [string]$Service, [string]$Proto, [int]$Priority, [int]$Weight, [int]$Port, [string]$Target, [int]$TTL) { $this.Name = $Name $this.Service = $Service $this.Proto = $Proto $this.Priority = $Priority $this.Weight = $Weight $this.Port = $Port $this.Target = $Target $this.TTL = if ($ttl) { $ttl } else { 1 } } CloudFlareRecordSRV () { $this.Name $this.Service $this.Proto $this.Priority $this.Weight $this.Port $this.Target $this.TTL = 1 } # Methods # CloudFlareRecordSRV ([object[]]$properties) # { # $this.Name = $Name # $this.Service = $Service # $this.Proto = $Proto # $this.Priority = $Priority # $this.Weight = $Weight # $this.Port = $Port # $this.Target = $Target # $this.TTL = if ($ttl) { $ttl } else { 1 } # } [Object]ToBody() { $objectData = [PSCustomObject][ordered]@{ type = 'SRV' data = [PSCustomObject][ordered]@{ "service" = $this.Service "proto" = $this.proto "name" = $this.name "priority" = $this.priority "weight" = $this.weight "port" = $this.port "target" = $this.target } ttl = $this.TTL } return $objectData } <# [void] Fill ( $Object ) { $this.Name = $Object.Name $this.Service = $Object.Service $this.Proto = $Object.Proto $this.Priority = $Object.Priority $this.Weight = $Object.Weight $this.Port = $Object.Port $this.Target = $Object.Target $this.TTL = $Object.Target } [void] Fill ( [string]$Name, [string]$Service, [string]$Proto, [int]$Priority, [int]$Weight, [int]$Port, [string]$Target, [string]$TTL ) { $this.Name = $Name $this.Service = $Service $this.Proto = $Proto $this.Priority = $Priority $this.Weight = $Weight $this.Port = $Port $this.Target = $Target $this.TTL = $TTL } #> } #EndRegion '.\Classes\CloudFlareRecordSRV.ps1' 107 #Region '.\Classes\CloudflareRecordTXT.ps1' 0 class CloudflareRecordTXT : CloudFlareRecord { # Properties [string]$Name [string]$Content [int]$TTL # Methods CloudFlareRecordTXT ([string]$Name, [string]$Content, [int]$TTL) { $this.Name = $Name $this.Content = $Content $this.ttl = if ($ttl) { $ttl } else { 1 } } CloudFlareRecordTXT () { $this.Name $this.Content $this.ttl = 1 } [Object]ToBody() { $objectData = [PSCustomObject][ordered]@{ type = "TXT" name = $this.name content = $this.Content ttl = $this.TTL } return $objectData } } #EndRegion '.\Classes\CloudflareRecordTXT.ps1' 35 #Region '.\Private\ConvertFrom-Base64.ps1' 0 function ConvertFrom-Base64 { [CmdletBinding()] param ( [Parameter(ValueFromPipeline)] [string] $string, [switch]$asBytes ) process { if ($asBytes) { return ([byte[]][System.Convert]::FromBase64String($string)) } else { return [string](([System.Convert]::FromBase64String($string) | % { [char]$_ }) -join '') } } } #EndRegion '.\Private\ConvertFrom-Base64.ps1' 24 #Region '.\Private\ConvertTo-Base64.ps1' 0 function ConvertTo-Base64 { [CmdletBinding()] param ( [Parameter(ValueFromPipeline)] [string] $string ) process { [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($string)) } } #EndRegion '.\Private\ConvertTo-Base64.ps1' 15 #Region '.\Public\Get-CloudflareDnsZone.ps1' 0 function Get-CloudflareDnsZone() { [CmdletBinding()] Param ( [Parameter(Position = 0)] $All, $Zone ) begin { if (!$cfApiToken) { Throw "No token. Use the 'Set-CloudflareApiToken' command to add the Token." } else { $token = ($cfApiToken | ConvertFrom-Base64) } } process { $All = $true $headers = @{ 'Authorization' = "Bearer $token" } $baseUri = "https://api.cloudflare.com/client/v4/zones" if ($Zone) { $cfUriGetZones = $baseUri + "?name=$zone" $zoneInfo = irm -Uri $cfUriGetZones -Headers $headers -ContentType 'application/json' $zoneInfo.result } else { $zoneInfo = irm -Uri $baseUri -Headers $headers -ContentType 'application/json' $zoneInfo.result Write-Host -f Yellow "FEATURE not implemented" } } } #EndRegion '.\Public\Get-CloudflareDnsZone.ps1' 38 #Region '.\Public\Get-CloudflareDnsZoneRecord.ps1' 0 function Get-CloudflareDnsZoneRecord() { [CmdletBinding()] Param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('id')] [string[]] $Zone ) begin { if (!$cfApiToken) { Throw "No token. Use the 'Set-CloudflareApiToken' command to add the Token." } else { $token = ($cfApiToken | ConvertFrom-Base64) } } process { $headers = @{ 'Authorization' = "Bearer $token" } $zoneId = if ($PSCmdlet.MyInvocation.ExpectingInput -eq $true) { $zone } else { (Get-CloudflareDnsZone -Zone $Zone).id } $cfUriGetZones = "https://api.cloudflare.com/client/v4/zones/$($zoneId)/dns_records" $zoneRecords = irm -Uri $cfUriGetZones -Headers $headers -ContentType 'application/json' $zoneRecords.result } } #EndRegion '.\Public\Get-CloudflareDnsZoneRecord.ps1' 37 #Region '.\Public\Invoke-CloudflareDnsZoneCommand.ps1' 0 function Invoke-CloudflareDnsZoneCommand { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'HIGH')] param ( [Parameter()] [uri]$Uri, [Parameter()] [string]$Body, [Parameter()] [ValidateSet("GET", "DELETE", "PATCH", "POST", "PUT")] $Method = "GET" ) try { if ($PSCmdlet.ShouldProcess("$uri", "$Method")) { if ($Method -in "PATCH", "DELETE", "PUT") { $rawContent = iwr -Uri "https://api.cloudflare.com/client/v4/zones/6d7c62048f94a6477d8c8b7d9cb1e6d4/dns_records" -Headers $headers -ContentType 'application/json' -Method $Method -Body $Body -ea 1 $encodedResult = [System.Text.Encoding]::UTF8.GetString($rawContent.RawContentStream.ToArray()) | ConvertFrom-Json -ea 1 $encodedResult.result } else { $rawContent = iwr -Uri "https://api.cloudflare.com/client/v4/zones/6d7c62048f94a6477d8c8b7d9cb1e6d4/dns_records" -Headers $headers -ContentType 'application/json' -Method $Method -Body $Body -ea 1 $encodedResult = [System.Text.Encoding]::UTF8.GetString($rawContent.RawContentStream.ToArray()) | ConvertFrom-Json -ea 1 $encodedResult.result } } } catch { $err = $_ $reader = New-Object System.IO.StreamReader($err.Exception.Response.GetResponseStream()) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $expandedErrorMsg = ($reader.ReadToEnd() | ConvertFrom-Json) if ($expandedErrorMsg.errors.error_chain) { $errorOutput = ($expandedErrorMsg.errors.error_chain | % { "[{0}] {1}" -f $_.code, $_.message }) -join "`n" Throw $errorOutput } else { Throw $expandedErrorMsg.errors.message } } } #EndRegion '.\Public\Invoke-CloudflareDnsZoneCommand.ps1' 53 #Region '.\Public\New-CloudflareDnsRecordObject.ps1' 0 function New-CloudflareDnsRecordObject() { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',DefaultParameterSetName = 'Blank')] Param ( [Parameter(Mandatory = $true, Position = 1, HelpMessage = "The DNS pointer's RR type.")] [DNSTypes]$RecordType ) DynamicParam { $props = Invoke-Command -ScriptBlock ([Scriptblock]::Create(("[CloudflareRecord{0}].GetProperties()" -f $RecordType))) $dynParams = @() foreach ($prop in $props) { $dynParams += [pscustomobject]@{Type = $recordType ; ParameterName = $prop.Name; DataType = $prop.PropertyType ; ValidateSet = $null } } $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary foreach ($param in $dynParams) { #ParameterAttribute Objects $Attr = New-Object System.Management.Automation.ParameterAttribute $Attr.Mandatory = $true $Attr.ParameterSetName = $RecordType # $Attr.HelpMessage = $param.HelpMessage #Attributecollection object for the attributes we just created. $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] #add our custom attributes $attributeCollection.Add($Attr) if ($param.ValidateSet) { # Generate and set the ValidateSet $arrSet = $param.ValidateSet $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) # Add the ValidateSet to the attributes collection $AttributeCollection.Add($ValidateSetAttribute) } #add our parameter specifying the attribute collection $definedParam = New-Object System.Management.Automation.RuntimeDefinedParameter($param.ParameterName, $param.DataType, $attributeCollection) #$pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection) #expose the name of our parameter # $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add($param.ParameterName, $definedParam) #$paramDictionary.Add('password', $pwParam) } return $paramDictionary } process { if ($pscmdlet.ParameterSetName -eq 'Blank') { $Object = Invoke-Command -ScriptBlock ([Scriptblock]::Create(("[CloudflareRecord{0}]::New()" -f $RecordType))) Return $Object } else { $Object = Invoke-Command -ScriptBlock ([Scriptblock]::Create(("[CloudflareRecord{0}]::New()" -f $RecordType))) $Object.GetPropertyNames() | % { $Object.$_ = $PSBoundParameters.$_ } Return $Object } } } #EndRegion '.\Public\New-CloudflareDnsRecordObject.ps1' 68 #Region '.\Public\New-CloudflareDnsZoneRecord.ps1' 0 <# .SYNOPSIS Add Cloudflare DNS records to a specified zone. .DESCRIPTION Add Cloudflare DNS records to a specified zone. .EXAMPLE C:\> New-CloudflareDnsZoneRecord -Zone "zonename.com" -Type SRV -Name testSrv -Service _service -Proto _tcp -Priority 12 -Weight 120 -Port 1231 -Target vg.no -TTL 3600 Adds a new SRV record for to target "1.2.3.4" with TTL set to 1 hour (3600 seconds) .EXAMPLE C:\> $obj = [CloudflareRecordA]::new('test19','1.2.3.4',$false,1) C:\> New-CloudflareDnsZoneRecord -Zone "zonename.com" -RecordObject $obj Adds a new A record for subdomain "test19" to target "1.2.3.4" with no proxy and TTL set to Auto .INPUTS Inputs (if any) .OUTPUTS Output (if any) .NOTES General notes #> function New-CloudflareDnsZoneRecord() { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] Param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 1)] [Alias('id')] [string[]] $Zone, [Parameter(ParameterSetName = "ManualInput", Mandatory = $true, Position = 2, HelpMessage = "The DNS pointer's RR type. Must be one of: A AAAA CNAME ANAME TLSA MX SRV DS CAA NS TXT.")] [DNSTypes]$Type, [Parameter(ParameterSetName = "ObjectInput", Mandatory = $true, Position = 2, HelpMessage = "Record object with a CloudflareRecord type")] [CloudflareRecord]$RecordObject # [ValidateSet('A', 'AAAA', 'CNAME', 'ANAME', 'TLSA', 'MX', 'SRV', 'DS', 'CAA', 'NS', 'TXT')]$Type, # [Parameter(Mandatory, Position = 2)] # [String] # $Name, # [Parameter(Mandatory = $true, Position = 3, HelpMessage = "Value / data. Valid values depend on the pointer's RR type.")] # $Content, # [Parameter(Mandatory = $False, Position = 4, HelpMessage = "TTL in seconds. 1 for AUTO")] # $TTL = 1, # [Parameter()] # [bool] # $Proxied ) DynamicParam { # $dynParams = @() # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Priority'; DataType = [int] ; ValidateSet = $null ; HelpMessage = 'Required for MX and SRV pointers. Preference/Priority for the pointer.' } # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Weight'; DataType = [int] ; ValidateSet = $null ; HelpMessage = 'Required for SRV pointers. A number value that indicates the weight of the pointer.' } # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Port'; DataType = [int] ; ValidateSet = $null ; HelpMessage = 'Required for SRV pointers. A port number for the SRV pointer.' } # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Service'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for SRV pointers. A port number for the SRV pointer.' } # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Proto'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for SRV pointers. A port number for the SRV pointer.' } # $dynParams += [pscustomobject]@{Type = "SRV" ; ParameterName = 'Target'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for SRV pointers. A port number for the SRV pointer.' } # $dynParams += [pscustomobject]@{Type = "TLSA" ; ParameterName = 'Usage'; DataType = [int] ; ValidateSet = 1, 2, 3 ; HelpMessage = 'Required for TLSA pointers. TLSA usage. Numeric value, 1 , 2 or 3.' } # $dynParams += [pscustomobject]@{Type = "TLSA" ; ParameterName = 'Selector'; DataType = [int] ; ValidateSet = 0, 1 ; HelpMessage = 'Required for TLSA pointers. TLSA selector. Numeric value, 0 or 1.' } # $dynParams += [pscustomobject]@{Type = "TLSA" ; ParameterName = 'Dtype'; DataType = [int] ; ValidateSet = 0, 1, 2 ; HelpMessage = 'Required for TLSA pointers. TLSA matching type. Numeric value, 0, 1 or 2.' } # $dynParams += [pscustomobject]@{Type = "DS" ; ParameterName = 'Tag'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for DS and CAA pointers.' } # $dynParams += [pscustomobject]@{Type = "DS" ; ParameterName = 'Alg'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for DSpointers. See RFC3658 for details.' } # $dynParams += [pscustomobject]@{Type = "DS" ; ParameterName = 'Digest'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for DSpointers. See RFC3658 for details.' } # $dynParams += [pscustomobject]@{Type = "CAA" ; ParameterName = 'Tag'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for DS and CAA pointers.' } # $dynParams += [pscustomobject]@{Type = "CAA" ; ParameterName = 'Flags'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for CAA pointers. See RFC3658 for details.' } # $dynParams += [pscustomobject]@{Type = "MX"; ParameterName = 'Priority'; DataType = [string] ; ValidateSet = $null ; HelpMessage = 'Required for MX and SRV pointers. Preference/Priority for the pointer.' } $dynParams = @() foreach ($recordType in ([DNSTypes].GetFields() | ? isStatic).Name) { $props = Invoke-Command -ScriptBlock ([Scriptblock]::Create(("[CloudflareRecord{0}].GetProperties()" -f $recordType))) foreach ($prop in $props) { $dynParams += [pscustomobject]@{Type = $recordType ; ParameterName = $prop.Name; DataType = $prop.PropertyType ; ValidateSet = $null } } } $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary foreach ($param in ($dynParams | ? Type -EQ $Type)) { #ParameterAttribute Objects $Attr = New-Object System.Management.Automation.ParameterAttribute $Attr.Mandatory = $true $Attr.ParameterSetName = "ManualInput" # $Attr.HelpMessage = $param.HelpMessage #Attributecollection object for the attributes we just created. $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] #add our custom attributes $attributeCollection.Add($Attr) if ($param.ValidateSet) { # Generate and set the ValidateSet $arrSet = $param.ValidateSet $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) # Add the ValidateSet to the attributes collection $AttributeCollection.Add($ValidateSetAttribute) } #add our parameter specifying the attribute collection $definedParam = New-Object System.Management.Automation.RuntimeDefinedParameter($param.ParameterName, $param.DataType, $attributeCollection) #$pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection) #expose the name of our parameter # $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add($param.ParameterName, $definedParam) #$paramDictionary.Add('password', $pwParam) } return $paramDictionary } begin { if (!$cfApiToken) { Throw "No token. Use the 'Set-CloudflareApiToken' command to add the Token." } else { $token = ($cfApiToken | ConvertFrom-Base64) } } process { $headers = @{ 'Authorization' = "Bearer $token" } $zoneId = if ($PSCmdlet.MyInvocation.ExpectingInput -eq $true) { $zone } else { (Get-CloudflareDnsZone -Zone $Zone).id } $cfUriGetZones = "https://api.cloudflare.com/client/v4/zones/$($zoneId)/dns_records" # Creating record switch ($PSCmdlet.ParameterSetName) { "ManualInput" { $RecordObj = New-CloudflareDnsRecordObject -RecordType $Type $RecordObj.GetPropertyNames() | % { $RecordObj.$_ = $PSBoundParameters.$_ } } "ObjectInput" { $RecordObj = $RecordObject $Type = $RecordObj.ToBody().type } Default { Throw "Unknown parameterset used." } } $operationInfo = ((@("Type = $Type") + ($RecordObj.psobject.Properties | % { "{0} [{1}]" -f $_.Name, $_.Value })) -join " // ") Write-Verbose $RecordObj.ToJson() if ($PSCmdlet.ShouldProcess($zoneId, "Adding $operationInfo")) { # $newZoneInfo = irm -Method Post -Body $RecordObj.ToJson() -Uri $cfUriGetZones -Headers $headers -ContentType 'application/json' $newZoneInfo = Invoke-CloudflareDnsZoneCommand -Uri $cfUriGetZones -Body $RecordObj.ToJson() -Method POST if ($newZoneInfo.sucess -eq $true) { $newZoneInfo.result } } } } #EndRegion '.\Public\New-CloudflareDnsZoneRecord.ps1' 168 #Region '.\Public\Set-CloudflareApiToken.ps1' 0 function Set-CloudflareApiToken { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [string] $Token ) process { $b64Token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($token)) $Global:cfApiToken = $b64Token } } #EndRegion '.\Public\Set-CloudflareApiToken.ps1' 16 #Region '.\Public\Test-CloudflareApi.ps1' 0 function Test-CloudflareApi() { [CmdletBinding()] Param () if ($cfApiToken) { $Token = [string](([System.Convert]::FromBase64String($cfApiToken) | % { [char]$_ }) -join '') $headers = @{ 'Authorization' = "Bearer $token" } $cfUriVerifyToken = "https://api.cloudflare.com/client/v4/user/tokens/verify" try { $verify = irm -Uri $cfUriVerifyToken -Headers $headers -ContentType 'application/json' if ($verify.success -eq $true) { $verify } } catch { Throw $_ } } else { Throw "No token found. Connect to Cloudflare by using 'Set-CloudflareApiToken' first." } } #EndRegion '.\Public\Test-CloudflareApi.ps1' 31 |