#!/usr/bin/env bash set -euo pipefail # ------------------------------- # Detect Terraform binary: tofu vs terraform # ------------------------------- if command -v tofu &>/dev/null; then TF_BIN="tofu" elif command -v terraform &>/dev/null; then TF_BIN="terraform" else echo "❌ Neither 'terraform' nor 'tofu' found in PATH" exit 1 fi echo "â„šī¸ Using $TF_BIN for Terraform operations" # ------------------------------- # Ensure CF API token # ------------------------------- CF_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}" if [[ -z "${CF_API_TOKEN}" ]]; then echo "Please set CF_API_TOKEN before running this script." exit 1 fi # ------------------------------- # Helper: fetch paginated results from Cloudflare API # ------------------------------- cf_paginate() { local endpoint="$1" local page=1 local per_page=100 while :; do local result result=$(curl -s -X GET "${endpoint}?page=${page}&per_page=${per_page}" \ -H "Authorization: Bearer ${CF_API_TOKEN}" \ -H "Content-Type: application/json") local items items=$(echo "${result}" | jq -r '.result[]? | @base64') [[ -z "$items" ]] && break echo "$items" local total_pages total_pages=$(echo "$result" | jq -r '.result_info.total_pages') ((page++)) [[ $page -gt $total_pages ]] && break done } # ------------------------------- # Generate Cloudflare resources using cf-terraforming # ------------------------------- generate_resources() { echo "🔧 Generating Cloudflare resources via cf-terraforming..." local output_file="cloudflare_resource_gen.tf" > "${output_file}" resources=( cloudflare_account cloudflare_account_member cloudflare_account_subscription cloudflare_address_map cloudflare_argo_tiered_caching cloudflare_authenticated_origin_pulls cloudflare_authenticated_origin_pulls_certificate cloudflare_bot_management cloudflare_certificate_pack cloudflare_content_scanning_expression cloudflare_custom_hostname cloudflare_custom_hostname_fallback_origin cloudflare_d1_database cloudflare_dns_firewall cloudflare_dns_record cloudflare_dns_zone_transfers_acl cloudflare_dns_zone_transfers_incoming cloudflare_dns_zone_transfers_outgoing cloudflare_dns_zone_transfers_peer cloudflare_dns_zone_transfers_tsig cloudflare_email_routing_address cloudflare_email_routing_catch_all cloudflare_email_routing_dns cloudflare_email_routing_rule cloudflare_email_routing_settings cloudflare_filter cloudflare_healthcheck cloudflare_hostname_tls_setting cloudflare_keyless_certificate cloudflare_leaked_credential_check cloudflare_leaked_credential_check_rule cloudflare_list_item cloudflare_load_balancer cloudflare_load_balancer_monitor cloudflare_load_balancer_pool cloudflare_logpull_retention cloudflare_logpush_job cloudflare_magic_wan_static_route cloudflare_managed_transforms cloudflare_mtls_certificate cloudflare_notification_policy cloudflare_notification_policy_webhooks cloudflare_observatory_scheduled_test cloudflare_origin_ca_certificate cloudflare_page_rule cloudflare_page_shield_policy cloudflare_pages_domain cloudflare_pages_project cloudflare_queue cloudflare_queue_consumer cloudflare_r2_bucket cloudflare_r2_custom_domain cloudflare_r2_managed_domain cloudflare_rate_limit cloudflare_regional_hostname cloudflare_regional_tiered_cache cloudflare_registrar_domain cloudflare_ruleset cloudflare_snippet_rules cloudflare_snippets cloudflare_spectrum_application cloudflare_stream cloudflare_stream_key cloudflare_stream_live_input cloudflare_stream_watermark cloudflare_stream_webhook cloudflare_tiered_cache cloudflare_total_tls cloudflare_turnstile_widget cloudflare_url_normalization_settings cloudflare_user cloudflare_waiting_room cloudflare_waiting_room_event cloudflare_waiting_room_rules cloudflare_waiting_room_settings cloudflare_web3_hostname cloudflare_web_analytics_rule cloudflare_web_analytics_site cloudflare_workers_cron_trigger cloudflare_workers_custom_domain cloudflare_workers_deployment cloudflare_workers_for_platforms_dispatch_namespace cloudflare_workers_kv_namespace cloudflare_workers_script_subdomain cloudflare_zone cloudflare_zone_cache_reserve cloudflare_zone_cache_variants cloudflare_zone_dnssec cloudflare_zone_lockdown cloudflare_zone_setting ) for r in "${resources[@]}"; do echo "Generating $r ..." cf-terraforming generate \ --token "${CF_API_TOKEN}" \ --resource-type "${r}" >> "${output_file}" || true done echo "✅ Terraform resources generated in ${output_file}" } # ------------------------------- # Import Cloudflare resources into state using cf-terraforming # ------------------------------- import_zone_resources() { local zone_id="$1" local zone_name="$2" echo "âŗ Importing zone $zone_name ..." cf-terraforming import \ --token "${CF_API_TOKEN}" \ --modern-import-block \ --resource-type cloudflare_zone \ --resource-id "$zone_id" >> cloudflare_resource_imp.tf || true echo "✅ Imported cloudflare_zone for $zone_name" echo "🔄 Importing DNS records for $zone_name ..." cf-terraforming import \ --token "${CF_API_TOKEN}" \ --zone "$zone_id" \ --modern-import-block \ --resource-type cloudflare_dns_record >> cloudflare_resource_imp.tf || true echo "✅ Imported DNS records for $zone_name" # Optional: import other zone-level resources for res in cloudflare_argo_tiered_caching cloudflare_email_routing_settings cloudflare_tiered_cache cloudflare_zone_dnssec; do cf-terraforming import \ --token "${CF_API_TOKEN}" \ --resource-type "$res" \ --modern-import-block \ --resource-id "$zone_id" >> cloudflare_resource_imp.tf || true echo "✅ Imported $res for $zone_name" done } # ------------------------------- # Main # ------------------------------- echo "Choose an option:" echo "1) Generate Cloudflare Terraform resources" echo "2) Import Cloudflare Terraform resources into state" read -rp "Enter 1 or 2: " choice case "$choice" in 1) generate_resources ;; 2) echo "🔄 Fetching zones..." zones=$(cf_paginate "https://api.cloudflare.com/client/v4/zones") declare -A zone_map while read -r z; do zname=$(echo "$z" | base64 --decode | jq -r '.name') zid=$(echo "$z" | base64 --decode | jq -r '.id') zone_map["$zname"]="$zid" done <<< "$zones" echo "⚡ Found ${#zone_map[@]} zones." for zone_name in "${!zone_map[@]}"; do zid="${zone_map[$zone_name]}" import_zone_resources "$zid" "$zone_name" done ;; *) echo "Invalid option. Enter 1 or 2." exit 1 ;; esac echo "🎉 All operations completed!"