← back to Ga Allsites
grant script: add GoDaddy DNS support (append TXT via API) so non-Cloudflare microsites can be verified too
a648e3c716e894400acd95323a316f6b3af53818 · 2026-08-17 09:59:26 -0700 · Steve Abrams
Files touched
M grant_microsites_gsc.py
Diff
commit a648e3c716e894400acd95323a316f6b3af53818
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 17 09:59:26 2026 -0700
grant script: add GoDaddy DNS support (append TXT via API) so non-Cloudflare microsites can be verified too
---
grant_microsites_gsc.py | 84 ++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 65 insertions(+), 19 deletions(-)
diff --git a/grant_microsites_gsc.py b/grant_microsites_gsc.py
index 07fce90..fdadf0a 100644
--- a/grant_microsites_gsc.py
+++ b/grant_microsites_gsc.py
@@ -54,17 +54,68 @@ RECOMMENDED = [
]
-def cf_token() -> str:
- for k in ("CLOUDFLARE_API_TOKEN", "CF_API_TOKEN", "CF_TOKEN"):
+def _env(*keys):
+ """First matching value from process env, else from the secrets .env."""
+ for k in keys:
if os.environ.get(k):
return os.environ[k]
if SECRETS_ENV.exists():
for line in SECRETS_ENV.read_text().splitlines():
line = line.strip()
- for k in ("CLOUDFLARE_API_TOKEN", "CF_API_TOKEN", "CF_TOKEN"):
+ for k in keys:
if line.startswith(k + "="):
return line.split("=", 1)[1].strip().strip('"').strip("'")
- raise SystemExit("ERROR: no Cloudflare API token in env or secrets-manager/.env")
+ return ""
+
+
+def cf_token() -> str:
+ t = _env("CLOUDFLARE_API_TOKEN", "CF_API_TOKEN", "CF_TOKEN")
+ if not t:
+ raise SystemExit("ERROR: no Cloudflare API token in env or secrets-manager/.env")
+ return t
+
+
+def godaddy_creds():
+ key = _env("GODADDY_API_KEY", "GODADDY_KEY")
+ sec = _env("GODADDY_API_SECRET", "GODADDY_SECRET")
+ return (key, sec) if key and sec else (None, None)
+
+
+def write_txt(domain: str, txt: str, cftok: str, gd) -> str:
+ """Upsert the apex TXT to whichever host is authoritative. Returns
+ 'present' | 'written' | 'no-host'."""
+ # Cloudflare — only if the zone is ACTIVE (pending zones aren't authoritative)
+ zones = cf(f"https://api.cloudflare.com/client/v4/zones?name={domain}", cftok)
+ z = (zones.get("result") or [None])[0]
+ if z and z.get("status") == "active":
+ zid = z["id"]
+ existing = cf(f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records"
+ f"?type=TXT&name={domain}", cftok)
+ if any(r["content"].strip('"') == txt for r in existing.get("result", [])):
+ return "present"
+ cf(f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records", cftok,
+ {"type": "TXT", "name": domain, "content": txt, "ttl": 120})
+ print(f" [{domain}] TXT written to Cloudflare (zone {zid[:8]}…)")
+ return "written"
+ # GoDaddy (domaincontrol.com nameservers) — APPEND via PATCH (never clobbers SPF/etc.)
+ gkey, gsec = gd
+ if gkey and gsec:
+ hdr = {"Authorization": f"sso-key {gkey}:{gsec}", "Content-Type": "application/json"}
+ base = f"https://api.godaddy.com/v1/domains/{domain}/records"
+ # read existing @ TXT records (GoDaddy uses sso-key auth, not bearer)
+ req = urllib.request.Request(f"{base}/TXT/%40", headers=hdr)
+ try:
+ cur = json.loads(urllib.request.urlopen(req, timeout=30).read() or "[]")
+ except Exception:
+ cur = []
+ if any(r.get("data", "").strip('"') == txt for r in cur):
+ return "present"
+ body = json.dumps([{"type": "TXT", "name": "@", "data": txt, "ttl": 600}]).encode()
+ preq = urllib.request.Request(base, data=body, headers=hdr, method="PATCH")
+ urllib.request.urlopen(preq, timeout=30).read()
+ print(f" [{domain}] TXT appended at GoDaddy")
+ return "written"
+ return "no-host"
def google_creds():
@@ -88,7 +139,7 @@ def cf(url, tok, payload=None, method=None):
return gget(url, tok, payload, method)
-def grant(domain: str, gtoken: str, cftok: str) -> str:
+def grant(domain: str, gtoken: str, cftok: str, gd) -> str:
# 1. mint DNS TXT token
tok = gget("https://www.googleapis.com/siteVerification/v1/token", gtoken, {
"verificationMethod": "DNS_TXT",
@@ -97,19 +148,12 @@ def grant(domain: str, gtoken: str, cftok: str) -> str:
txt = tok["token"]
print(f" [{domain}] token: {txt}")
- # 2. find CF zone + upsert the TXT record on the apex
- zones = cf(f"https://api.cloudflare.com/client/v4/zones?name={domain}", cftok)
- if not zones.get("result"):
- return "SKIP (no Cloudflare zone)"
- zid = zones["result"][0]["id"]
- existing = cf(f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records"
- f"?type=TXT&name={domain}", cftok)
- already = any(r["content"].strip('"') == txt for r in existing.get("result", []))
- if not already:
- cf(f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records", cftok,
- {"type": "TXT", "name": domain, "content": txt, "ttl": 120})
- print(f" [{domain}] TXT written to Cloudflare (zone {zid[:8]}…)")
- time.sleep(8) # let DNS propagate before asking Google to check
+ # 2. write the TXT to whichever host is authoritative (Cloudflare or GoDaddy)
+ state = write_txt(domain, txt, cftok, gd)
+ if state == "no-host":
+ return "SKIP (DNS not on active Cloudflare or GoDaddy)"
+ if state == "written":
+ time.sleep(12) # let DNS propagate before asking Google to check
else:
print(f" [{domain}] TXT already present")
@@ -148,13 +192,15 @@ def main():
creds = google_creds()
gtoken = creds.token
cftok = cf_token()
+ gd = godaddy_creds()
print(f"SA: {json.loads(KEY.read_text()).get('client_email')}")
+ print(f"GoDaddy API: {'available' if gd[0] else 'NOT configured'}")
print(f"Granting {len(targets)} domain(s): {', '.join(targets)}\n")
results = {}
for d in targets:
try:
- results[d] = grant(d, gtoken, cftok)
+ results[d] = grant(d, gtoken, cftok, gd)
except urllib.error.HTTPError as e:
results[d] = f"ERROR {e.code}: {e.read().decode()[:160]}"
except Exception as e: # noqa: BLE001
← 3709923 auto-data-snapshot: 2026-08-17T09:56:21 (1 data files) — etl
·
back to Ga Allsites
·
grant script: --all-fleet mode (proven — took GSC coverage 1 7f57305 →