Hudu: Document CloudFlare Zones
I have been meaning to put together a method of Documenting CloudFlare Zones to Hudo for a while now, but kept forgetting. I saw someone requesting this today on MSPGeek so thought I would put it together.
This script will document the Zone info, DNS entries, Firewall Rules, Page Rules and Zone settings into Hudu and link it to the matching website.
Setup
First login to CloudFlare, click my account in the top right and create a new API token with Read permissions on:
Zone -> Zone Settings
Zone -> Zone
Zone -> DNS
Zone -> Page Rules
Zone -> Firewall Services
Next make sure websites are already setup under each customer that match the Zones you want to import. Any that can’t be matched will be output at the end of the script.
Finally fill in the settings at the top of the script and run it.
The Script
The latest version can be found here: https://github.com/lwhitelock/HuduAutomation/blob/main/Cloudflare-Hudu.ps1
$VaultName = "Your-Key-Vault"
$VaultName = "Your-Key-Vault"
$CloudFlareHuduToken = Get-AzKeyVaultSecret -VaultName $VaultName -Name "CloudFlareHuduToken" -AsPlainText
$HuduAPIKey = Get-AzKeyVaultSecret -vaultName $VaultName -name "HuduAPIKey" -AsPlainText
$HuduBaseDomain = Get-AzKeyVaultSecret -vaultName $VaultName -name "HuduBaseDomain" -AsPlainText
$HuduAssetLayoutName = "CloudFlare Zones"
$BaseURL = 'https://api.cloudflare.com/client/v4'
function Get-CloudFlarePage {
param (
[string]$Uri
)
$Page = 0
[System.Collections.Generic.List[PSCustomObject]]$Array = @()
do {
$Page++
$Result = Invoke-RestMethod -URI "$($Uri)?per_page=50&page=$Page" -Method Get -Headers $AuthHeaders
$Result.result | foreach-object {
$Array.add($_)
}
} while ($Page -lt $Result.result_info.total_pages)
Return $Array
}
function Get-LinkBlock($URL, $Icon, $Title) {
return "<div class='o365__app' style='text-align:center'><a href=$URL target=_blank><h3><i class=`"$Icon`"> </i>$Title</h3></a></div>"
}
import-module HuduAPI
New-HuduAPIKey $HuduAPIKey
New-HuduBaseUrl $HuduBaseDomain
$Layout = Get-HuduAssetLayouts -name $HuduAssetLayoutName
if (!$Layout) {
$AssetLayoutFields = @(
@{
label = 'Link'
field_type = 'RichText'
show_in_list = 'false'
position = 1
},
@{
label = 'Status'
field_type = 'Text'
show_in_list = 'true'
position = 2
},
@{
label = 'Name Servers'
field_type = 'RichText'
show_in_list = 'false'
position = 3
},
@{
label = 'Original Name Servers'
field_type = 'RichText'
show_in_list = 'false'
position = 4
},
@{
label = 'Original Registrar'
field_type = 'RichText'
show_in_list = 'false'
position = 5
},
@{
label = 'Modified On'
field_type = 'Date'
show_in_list = 'true'
position = 6
},
@{
label = 'Account'
field_type = 'Text'
show_in_list = 'true'
position = 7
},
@{
label = 'Plan'
field_type = 'Text'
show_in_list = 'true'
position = 8
},
@{
label = 'Plan Cost'
field_type = 'Text'
show_in_list = 'true'
position = 9
},
@{
label = 'DNSSEC Status'
field_type = 'Text'
show_in_list = 'true'
position = 10
},
@{
label = 'DNS Records'
field_type = 'RichText'
show_in_list = 'true'
position = 11
},
@{
label = 'Zone Settings'
field_type = 'RichText'
show_in_list = 'true'
position = 12
},
@{
label = 'Firewall Rules'
field_type = 'RichText'
show_in_list = 'true'
position = 13
},
@{
label = 'Page Rules'
field_type = 'RichText'
show_in_list = 'true'
position = 14
},
@{
label = 'BIND File'
field_type = 'RichText'
show_in_list = 'true'
position = 14
}
)
Write-Host "Creating New Asset Layout $HuduAssetLayoutName"
$null = New-HuduAssetLayout -name $HuduAssetLayoutName -icon "fas fa-sitemap" -color "#e6892a" -icon_color "#ffffff" -include_passwords $false -include_photos $false -include_comments $false -include_files $false -fields $AssetLayoutFields
$Layout = Get-HuduAssetLayouts -name $HuduAssetLayoutName
}
$Websites = Get-HuduWebsites
$ParsedSites = $Websites.name | ForEach-Object { $_ -replace 'https://', '' }
$AuthHeaders = @{
'Authorization' = "Bearer $CloudFlareHuduToken"
}
$Zones = Get-CloudFlarePage -URI "$BaseURL/zones"
[System.Collections.Generic.List[PSCustomObject]]$UnmatchedZones = @()
foreach ($Zone in $Zones) {
try {
if ($Zone.name -in $ParsedSites) {
# DNS Records
$Website = $Websites | where-object { "https://$($Zone.name)" -eq $_.name }
if (($Website | measure-object).count -eq 1) {
$ZoneRecords = Get-CloudFlarePage -URI "$BaseURL/zones/$($Zone.ID)/dns_records"
$ZoneHTML = $ZoneRecords | Select-Object @{N = 'Name'; E = { $_.name } }, @{N = 'Type'; E = { $_.type } }, @{N = 'Content'; E = { $_.content } }, @{N = 'Proxied'; E = { $_.proxied } }, @{N = 'TTL'; E = { $_.ttl } }, @{N = 'Modified'; E = { $_.modified_on } } | convertto-html -as Table -Fragment | out-String
$ZoneSettings = Get-CloudFlarePage -URI "$BaseURL/zones/$($Zone.ID)/settings"
$ZoneSettingsHTML = $ZoneSettings | Select-Object @{N = 'Setting'; E = { $_.id } },@{N = 'Value'; E = { $_.value } },@{N = 'Modified'; E = { $_.modified_on } } | convertto-html -as Table -Fragment | out-string
$DNSSec = Get-CloudFlarePage -URI "$BaseURL/zones/$($Zone.ID)/dnssec"
$FirewallRules = Get-CloudFlarePage -URI "$BaseURL/zones/$($Zone.ID)/firewall/rules" | convertto-html -as Table -Fragment | out-string
$PageRules = Get-CloudFlarePage -URI "$BaseURL/zones/$($Zone.ID)/pagerules" | convertto-html -as Table -Fragment | out-string
$CloudflareLink = Get-LinkBlock -URL "https://dash.cloudflare.com/$($Zone.account.id)/$($Zone.name)" -Icon "far fa-cloud" -Title "Open in CloudFlare"
$Response = Invoke-WebRequest -Headers $AuthHeaders -Uri "$BaseURL/zones/$($Zone.ID)/dns_records/export" -Method Get
$BindFile = [System.Text.Encoding]::UTF8.GetString($response.Content)
$AssetFields = @{
'link' = $CloudflareLink
'status' = $Zone.status
'name_servers' = $Zone.name_servers -join ', '
'original_name_servers' = $Zone.original_name_servers -join ', '
'original_registrar' = $Zone.original_registrar
'modified_on' = $Zone.modified_on
'account' = $Zone.account.name
'plan' = $Zone.plan.name
'plan_cost' = "$($Zone.plan.price) $($Zone.plan.currency)"
'dnssec_status' = $DNSSec.status
'dns_records' = $ZoneHTML
'firewall_rules' = $FirewallRules
'page_rules' = $PageRules
'zone_settings' = $ZoneSettingsHTML
'bind_file' = "<pre>$BindFile</pre>"
}
$AssetName = $Zone.name
$CompanyID = $Website.company_id
$Asset = Get-HuduAssets -name $AssetName -companyid $CompanyID -assetlayoutid $Layout.id
if (!$Asset) {
Write-Host "Creating new Asset - $($zone.name)"
$Asset = New-HuduAsset -name $AssetName -company_id $CompanyID -asset_layout_id $Layout.id -fields $AssetFields
}
else {
Write-Host "Updating Asset - $($zone.name)"
$Asset = Set-HuduAsset -asset_id $Asset.id -name $AssetName -company_id $CompanyID -asset_layout_id $Layout.id -fields $AssetFields
}
try {
$null = New-HuduRelation -FromableType "Asset" -FromableId $Asset.asset.id -ToableType "Website" -ToableId $Website.id -ea stop
} catch {
# "Relation already exists"
}
} else {
Throw "Failed to match to a single website"
}
} else {
$UnmatchedZones.add($Zone)
}
} catch {
Write-Error "Failed processing zone $($Zone.name): $_"
}
}
Write-Host "The following domains were not matched to a website in Hudu. Please add a website under the correct customer for them"
$UnmatchedZones | Select-Object name, account