Automate Cloudflare DNS management with PowerShell
Cloudflare is a popular service for managing DNS records, providing security features, and optimizing website performance. Automating DNS management tasks can save time and reduce the risk of human error. In this article, I will explore how to use PowerShell to automate Cloudflare DNS management tasks.
Cloudflare API
To interact with Cloudflare's DNS management features, we will use the Cloudflare API. The API allows you to programmatically manage DNS records, zones, and other settings.
To get started, you need to create an API token in your Cloudflare account. Follow these steps: Create API token
Cloudflare ZoneId
To manage DNS records, you need to know the Zone ID of your domain. The Zone ID is not the name of your domain, but a unique identifier assigned by Cloudflare. You can find the Zone ID in the Cloudflare dashboard under the "Overview" tab - but we want to automate this via PowerShell.
# Author: Benjamin Abt (https://benjamin-abt.com)
# Script: cf-get-domain-zoneid.ps1
# Purpose: Retrieves the Zone ID for a specified domain from Cloudflare using their API
# Usage:
# $zoneInfo = .\cf-get-domain-zoneid.ps1 -ApiToken "your-cloudflare-api-token" -DomainName "example.com" | ConvertFrom-Json
# Write-Host "Domain: $($zoneInfo.Domain)"
# Write-Host "Zone ID: $($zoneInfo.ZoneId)"
param(
[Parameter(Mandatory = $true)]
[string]$ApiToken,
[Parameter(Mandatory = $true)]
[string]$DomainName
)
# Setup headers for Cloudflare API authentication
$headers = @{
"Authorization" = "Bearer $ApiToken"
"Content-Type" = "application/json"
}
# Construct the API URL to query zones filtered by domain name
$url = "https://api.cloudflare.com/client/v4/zones?name=$DomainName"
# Send GET request to Cloudflare API
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
# Check if the request was successful and results were returned
if ($response.success -and $response.result.Count -gt 0) {
# Create a custom object with domain and zone ID information
$data = [PSCustomObject]@{
Domain = $DomainName # Store the domain name
ZoneId = $response.result[0].id # Extract the zone ID from the response
}
# Return the data as a JSON object
return $data | ConvertTo-Json -Depth 2
} else {
# Throw an error if the domain was not found
throw "Failed to retrieve Zone ID for domain '$DomainName'. Check if the domain exists in your Cloudflare account."
}
To use the script, save it as cf-get-domain-zoneid.ps1
and run it with the following command:
$zoneInfo = .\cf-get-domain-zoneid.ps1 -ApiToken "your-cloudflare-api-token" -DomainName "example.com" | ConvertFrom-Json
Write-Host "Domain: $($zoneInfo.Domain)"
Write-Host "Zone ID: $($zoneInfo.ZoneId)"
This will output the domain name and its corresponding Zone ID.
Cloudflare DNS Records
Once you have the Zone ID, you can manage DNS records for that domain. Below is a PowerShell script that demonstrates how to add and update using the Cloudflare API.
# ---------------------------------------------------------------------------
# Author: Benjamin Abt (https://benjamin-abt.com)
# Script: cf-set-dns-entry.ps1
# Description: Creates or updates DNS records in Cloudflare via the API.
# This script can set CNAME or TXT records with specified TTL and proxy settings.
# Usage:
# .\cf-set-dns-entry.ps1 `
# -ApiToken "CloudflareAPIToken123" `
# -ZoneId "abc123def456" `
# -Domain "example.com" `
# -Subdomain "www" `
# -RecordType "CNAME" `
# -RecordContent "origin.myproxy.com" `
# -Ttl "3600" `
# -Proxied "true"
# ---------------------------------------------------------------------------
param(
[Parameter(Mandatory = $true)]
[string]$ApiToken, # Cloudflare API token with DNS edit permissions
[Parameter(Mandatory = $true)]
[string]$ZoneId, # Cloudflare Zone ID for the domain
[Parameter(Mandatory = $true)]
[string]$Domain, # Base domain name, e.g. "example.com"
[Parameter(Mandatory = $true)]
[string]$Subdomain, # Subdomain label, e.g. "_acme-challenge" or "www" (use empty string for apex domain)
[Parameter(Mandatory = $true)]
[ValidateSet("CNAME", "TXT")]
[string]$RecordType, # Type of DNS record to create/update
[Parameter(Mandatory = $true)]
[string]$RecordContent, # Value for the DNS record
[Parameter()]
[ValidatePattern("^\d+$|^(?i:auto)$")]
[string]$Ttl = "3600", # Time-to-live in seconds, or "auto" for Cloudflare default
[Parameter()]
[ValidateSet("true", "false")]
[string]$Proxied = "false" # Whether the record should be proxied through Cloudflare
)
# Construct the full record name based on subdomain and domain
$RecordName = if ([string]::IsNullOrEmpty($Subdomain)) { $Domain } else { "$Subdomain.$Domain" }
# Set up API request headers
$headers = @{
"Authorization" = "Bearer $ApiToken"
"Content-Type" = "application/json"
}
# Convert TTL to proper format (1 is Cloudflare's "auto" TTL)
$ttlValue = if ($Ttl.ToLower() -eq "auto") { 1 } else { [int]$Ttl }
# Convert string "true"/"false" to boolean for JSON
$proxiedValue = $Proxied.ToLower() -eq "true"
# Build URL to check for existing records
$recordListUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records?type=$RecordType&name=$RecordName"
# Query Cloudflare API to check if record already exists
$response = Invoke-RestMethod -Method Get -Uri $recordListUrl -Headers $headers
# Check if the API call was successful
if (-not $response.success) {
throw "Error retrieving DNS records: $($response.errors[0].message)"
}
# Find an existing record that matches the name
$existingRecord = $response.result | Where-Object { $_.name -eq $RecordName }
# Prepare the request body for creating/updating the record
$body = @{
type = $RecordType
name = $RecordName
content = $RecordContent
ttl = $ttlValue
proxied = $proxiedValue
} | ConvertTo-Json -Depth 3
# If the record exists, update it; otherwise, create a new record
if ($existingRecord) {
Write-Host "Record exists. Updating..."
$updateUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records/$($existingRecord.id)"
$updateResponse = Invoke-RestMethod -Method Put -Uri $updateUrl -Headers $headers -Body $body
} else {
Write-Host "Record does not exist. Creating..."
$createUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records"
$updateResponse = Invoke-RestMethod -Method Post -Uri $createUrl -Headers $headers -Body $body
}
# Check if the create/update operation was successful
if ($updateResponse.success) {
Write-Host "$RecordType record successfully processed: $RecordName => $RecordContent"
} else {
throw "Error updating/creating DNS record: $($updateResponse.errors[0].message)"
}
To use the script, save it as cf-set-dns-entry.ps1
and run it with the following command:
.\cf-set-dns-entry.ps1 `
-ApiToken "CloudflareAPIToken123" `
-ZoneId "abc123def456" `
-Domain "example.com" `
-Subdomain "www" `
-RecordType "CNAME" `
-RecordContent "origin.myproxy.com" `
-Ttl "3600" `
-Proxied "true"
This will create or update a CNAME record for the specified subdomain.
DevOps
In a DevOps context, automating DNS management with PowerShell can be integrated into CI/CD pipelines. For example, you can use the above scripts to automatically update DNS records when deploying new versions of your application or when changing infrastructure.
In my case, I can now use this script to update my DNS records for the SchwabenCode website. I can integrate it into my CI/CD pipeline to ensure that the DNS records are always up to date when I make changes to my infrastructure. This is especially necessary with the Azure Frontdoor, as it does not offer automatic certificate renewal for APEX domains, but only does this manually in combination with a re-validation of the domain (via TXT entries) - every 150 days.
Conclusion
Automating DNS management with PowerShell and the Cloudflare API is a powerful way to save time and reduce human error. With the scripts provided, you can easily create, update and manage DNS records. This automation can be integrated into DevOps pipelines to ensure your DNS records are always up to date.