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.

 1# Author: Benjamin Abt (https://benjamin-abt.com)
 2# Script: cf-get-domain-zoneid.ps1
 3# Purpose: Retrieves the Zone ID for a specified domain from Cloudflare using their API
 4# Usage: 
 5#   $zoneInfo = .\cf-get-domain-zoneid.ps1 -ApiToken "your-cloudflare-api-token" -DomainName "example.com" | ConvertFrom-Json
 6#   Write-Host "Domain: $($zoneInfo.Domain)"
 7#   Write-Host "Zone ID: $($zoneInfo.ZoneId)"
 8
 9param(
10    [Parameter(Mandatory = $true)]
11    [string]$ApiToken,
12
13    [Parameter(Mandatory = $true)]
14    [string]$DomainName
15)
16
17# Setup headers for Cloudflare API authentication
18$headers = @{
19    "Authorization" = "Bearer $ApiToken"
20    "Content-Type"  = "application/json"
21}
22
23# Construct the API URL to query zones filtered by domain name
24$url = "https://api.cloudflare.com/client/v4/zones?name=$DomainName"
25
26# Send GET request to Cloudflare API
27$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
28
29# Check if the request was successful and results were returned
30if ($response.success -and $response.result.Count -gt 0) {
31    # Create a custom object with domain and zone ID information
32    $data = [PSCustomObject]@{
33        Domain = $DomainName  # Store the domain name
34        ZoneId = $response.result[0].id  # Extract the zone ID from the response
35    }
36
37    # Return the data as a JSON object
38    return $data | ConvertTo-Json -Depth 2
39
40} else {
41    # Throw an error if the domain was not found
42    throw "Failed to retrieve Zone ID for domain '$DomainName'. Check if the domain exists in your Cloudflare account."
43}

To use the script, save it as cf-get-domain-zoneid.ps1 and run it with the following command:

1$zoneInfo = .\cf-get-domain-zoneid.ps1 -ApiToken "your-cloudflare-api-token" -DomainName "example.com" | ConvertFrom-Json
2Write-Host "Domain: $($zoneInfo.Domain)"
3Write-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.

  1# ---------------------------------------------------------------------------
  2# Author: Benjamin Abt (https://benjamin-abt.com)
  3# Script: cf-set-dns-entry.ps1
  4# Description: Creates or updates DNS records in Cloudflare via the API.
  5# This script can set CNAME or TXT records with specified TTL and proxy settings.
  6# Usage: 
  7#   .\cf-set-dns-entry.ps1 `
  8#     -ApiToken "CloudflareAPIToken123" `
  9#     -ZoneId "abc123def456" `
 10#     -Domain "example.com" `
 11#     -Subdomain "www" `
 12#     -RecordType "CNAME" `
 13#     -RecordContent "origin.myproxy.com" `
 14#     -Ttl "3600" `
 15#     -Proxied "true"
 16# ---------------------------------------------------------------------------
 17
 18param(
 19    [Parameter(Mandatory = $true)]
 20    [string]$ApiToken,    # Cloudflare API token with DNS edit permissions
 21
 22    [Parameter(Mandatory = $true)]
 23    [string]$ZoneId,      # Cloudflare Zone ID for the domain
 24
 25    [Parameter(Mandatory = $true)]
 26    [string]$Domain,      # Base domain name, e.g. "example.com"
 27
 28    [Parameter(Mandatory = $true)]
 29    [string]$Subdomain,   # Subdomain label, e.g. "_acme-challenge" or "www" (use empty string for apex domain)
 30
 31    [Parameter(Mandatory = $true)]
 32    [ValidateSet("CNAME", "TXT")]
 33    [string]$RecordType,  # Type of DNS record to create/update
 34
 35    [Parameter(Mandatory = $true)]
 36    [string]$RecordContent, # Value for the DNS record
 37
 38    [Parameter()]
 39    [ValidatePattern("^\d+$|^(?i:auto)$")]
 40    [string]$Ttl = "3600",  # Time-to-live in seconds, or "auto" for Cloudflare default
 41
 42    [Parameter()]
 43    [ValidateSet("true", "false")]
 44    [string]$Proxied = "false"  # Whether the record should be proxied through Cloudflare
 45)
 46
 47# Construct the full record name based on subdomain and domain
 48$RecordName = if ([string]::IsNullOrEmpty($Subdomain)) { $Domain } else { "$Subdomain.$Domain" }
 49
 50# Set up API request headers
 51$headers = @{
 52    "Authorization" = "Bearer $ApiToken"
 53    "Content-Type"  = "application/json"
 54}
 55
 56# Convert TTL to proper format (1 is Cloudflare's "auto" TTL)
 57$ttlValue = if ($Ttl.ToLower() -eq "auto") { 1 } else { [int]$Ttl }
 58# Convert string "true"/"false" to boolean for JSON
 59$proxiedValue = $Proxied.ToLower() -eq "true"
 60
 61# Build URL to check for existing records
 62$recordListUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records?type=$RecordType&name=$RecordName"
 63
 64# Query Cloudflare API to check if record already exists
 65$response = Invoke-RestMethod -Method Get -Uri $recordListUrl -Headers $headers
 66
 67# Check if the API call was successful
 68if (-not $response.success) {
 69    throw "Error retrieving DNS records: $($response.errors[0].message)"
 70}
 71
 72# Find an existing record that matches the name
 73$existingRecord = $response.result | Where-Object { $_.name -eq $RecordName }
 74
 75# Prepare the request body for creating/updating the record
 76$body = @{
 77    type    = $RecordType
 78    name    = $RecordName
 79    content = $RecordContent
 80    ttl     = $ttlValue
 81    proxied = $proxiedValue
 82} | ConvertTo-Json -Depth 3
 83
 84# If the record exists, update it; otherwise, create a new record
 85if ($existingRecord) {
 86    Write-Host "Record exists. Updating..."
 87    $updateUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records/$($existingRecord.id)"
 88    $updateResponse = Invoke-RestMethod -Method Put -Uri $updateUrl -Headers $headers -Body $body
 89} else {
 90    Write-Host "Record does not exist. Creating..."
 91    $createUrl = "https://api.cloudflare.com/client/v4/zones/$ZoneId/dns_records"
 92    $updateResponse = Invoke-RestMethod -Method Post -Uri $createUrl -Headers $headers -Body $body
 93}
 94
 95# Check if the create/update operation was successful
 96if ($updateResponse.success) {
 97    Write-Host "$RecordType record successfully processed: $RecordName => $RecordContent"
 98} else {
 99    throw "Error updating/creating DNS record: $($updateResponse.errors[0].message)"
100}

To use the script, save it as cf-set-dns-entry.ps1 and run it with the following command:

1.\cf-set-dns-entry.ps1 `
2    -ApiToken "CloudflareAPIToken123" `
3    -ZoneId "abc123def456" `
4    -Domain "example.com" `
5    -Subdomain "www" `
6    -RecordType "CNAME" `
7    -RecordContent "origin.myproxy.com" `
8    -Ttl "3600" `
9    -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.


Comments

Twitter Facebook LinkedIn WhatsApp