#!/usr/bin/env python3 """ Alibaba Cloud DNS Record Manager Supports: list, add, update, delete, enable, disable operations on Alidns records. Usage: python alidns_manager.py list --domain example.com [--rr www] [--type A] python alidns_manager.py add --domain example.com --rr www --type A --value 1.2.3.4 [--ttl 600] python alidns_manager.py update --record-id 123456 --rr www --type A --value 5.6.7.8 [--ttl 600] python alidns_manager.py delete --record-id 123456 python alidns_manager.py enable --record-id 123456 python alidns_manager.py disable --record-id 123456 Environment Variables: ALIBABA_CLOUD_ACCESS_KEY_ID - RAM sub-user Access Key ID ALIBABA_CLOUD_ACCESS_KEY_SECRET - RAM sub-user Access Key Secret """ import argparse import json import os import sys try: from alibabacloud_alidns20150109.client import Client as AlidnsClient from alibabacloud_alidns20150109 import models as alidns_models from alibabacloud_tea_openapi import models as open_api_models except ImportError: print("ERROR: Required packages not installed. Run:") print(" pip install alibabacloud_alidns20150109 alibabacloud_tea_openapi --break-system-packages") sys.exit(1) def create_client(region_id: str = "cn-hangzhou") -> AlidnsClient: """Create an authenticated Alidns client.""" access_key_id = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID") access_key_secret = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET") if not access_key_id or not access_key_secret: print("ERROR: Set ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET env vars.") sys.exit(1) config = open_api_models.Config( access_key_id=access_key_id, access_key_secret=access_key_secret, ) config.endpoint = f"alidns.{region_id}.aliyuncs.com" return AlidnsClient(config) def list_records(client: AlidnsClient, domain: str, rr: str = None, record_type: str = None, page_size: int = 100): """List all DNS records for a domain, with optional filtering.""" request = alidns_models.DescribeDomainRecordsRequest( domain_name=domain, page_size=page_size, ) if rr: request.rrkey_word = rr if record_type: request.type = record_type try: response = client.describe_domain_records(request) records = response.body.domain_records.record total = response.body.total_count print(f"\nπŸ“‹ DNS Records for {domain} (Total: {total})\n") print(f"{'RecordId':<20} {'RR':<25} {'Type':<8} {'Value':<40} {'TTL':<6} {'Status'}") print("-" * 120) for r in records: status_icon = "βœ…" if r.status == "ENABLE" else "⏸️" rr_display = f"{r.rr}.{domain}" if r.rr != "@" else domain print(f"{r.record_id:<20} {rr_display:<25} {r.type:<8} {r.value:<40} {r.ttl:<6} {status_icon} {r.status}") return records except Exception as e: print(f"ERROR listing records: {e}") sys.exit(1) def add_record(client: AlidnsClient, domain: str, rr: str, record_type: str, value: str, ttl: int = 600): """Add a new DNS record.""" request = alidns_models.AddDomainRecordRequest( domain_name=domain, rr=rr, type=record_type, value=value, ttl=ttl, ) try: response = client.add_domain_record(request) record_id = response.body.record_id fqdn = f"{rr}.{domain}" if rr != "@" else domain print(f"βœ… Added: {fqdn} -> {record_type} {value} (TTL: {ttl}, RecordId: {record_id})") return record_id except Exception as e: error_msg = str(e) if "DomainRecordDuplicate" in error_msg: print(f"⚠️ Record already exists: {rr}.{domain} {record_type} {value}") elif "DomainRecordConflict" in error_msg: print(f"❌ Conflict: CNAME records cannot coexist with other record types for {rr}.{domain}") else: print(f"ERROR adding record: {e}") return None def update_record(client: AlidnsClient, record_id: str, rr: str, record_type: str, value: str, ttl: int = 600): """Update an existing DNS record by RecordId.""" request = alidns_models.UpdateDomainRecordRequest( record_id=record_id, rr=rr, type=record_type, value=value, ttl=ttl, ) try: client.update_domain_record(request) print(f"βœ… Updated RecordId {record_id}: {rr} -> {record_type} {value} (TTL: {ttl})") except Exception as e: print(f"ERROR updating record: {e}") def delete_record(client: AlidnsClient, record_id: str): """Delete a DNS record by RecordId.""" request = alidns_models.DeleteDomainRecordRequest(record_id=record_id) try: client.delete_domain_record(request) print(f"βœ… Deleted RecordId: {record_id}") except Exception as e: print(f"ERROR deleting record: {e}") def set_record_status(client: AlidnsClient, record_id: str, status: str): """Enable or disable a DNS record.""" request = alidns_models.SetDomainRecordStatusRequest( record_id=record_id, status=status, # "Enable" or "Disable" ) try: client.set_domain_record_status(request) icon = "βœ…" if status == "Enable" else "⏸️" print(f"{icon} RecordId {record_id} status set to: {status}") except Exception as e: print(f"ERROR setting status: {e}") def main(): parser = argparse.ArgumentParser(description="Alibaba Cloud DNS Record Manager") subparsers = parser.add_subparsers(dest="action", help="Action to perform") # List list_parser = subparsers.add_parser("list", help="List DNS records") list_parser.add_argument("--domain", required=True, help="Domain name (e.g., gogenex.com)") list_parser.add_argument("--rr", help="Filter by subdomain prefix") list_parser.add_argument("--type", help="Filter by record type (A, CNAME, etc.)") list_parser.add_argument("--region", default="cn-hangzhou", help="Alidns region (default: cn-hangzhou)") # Add add_parser = subparsers.add_parser("add", help="Add a DNS record") add_parser.add_argument("--domain", required=True) add_parser.add_argument("--rr", required=True, help="Subdomain prefix (e.g., api, www, @)") add_parser.add_argument("--type", required=True, help="Record type (A, AAAA, CNAME, MX, TXT, etc.)") add_parser.add_argument("--value", required=True, help="Record value (IP, domain, text)") add_parser.add_argument("--ttl", type=int, default=600, help="TTL in seconds (default: 600)") add_parser.add_argument("--region", default="cn-hangzhou") # Update update_parser = subparsers.add_parser("update", help="Update a DNS record") update_parser.add_argument("--record-id", required=True, help="RecordId to update") update_parser.add_argument("--rr", required=True) update_parser.add_argument("--type", required=True) update_parser.add_argument("--value", required=True) update_parser.add_argument("--ttl", type=int, default=600) update_parser.add_argument("--region", default="cn-hangzhou") # Delete delete_parser = subparsers.add_parser("delete", help="Delete a DNS record") delete_parser.add_argument("--record-id", required=True, help="RecordId to delete") delete_parser.add_argument("--region", default="cn-hangzhou") # Enable / Disable enable_parser = subparsers.add_parser("enable", help="Enable a DNS record") enable_parser.add_argument("--record-id", required=True) enable_parser.add_argument("--region", default="cn-hangzhou") disable_parser = subparsers.add_parser("disable", help="Disable a DNS record") disable_parser.add_argument("--record-id", required=True) disable_parser.add_argument("--region", default="cn-hangzhou") args = parser.parse_args() if not args.action: parser.print_help() sys.exit(1) client = create_client(args.region) if args.action == "list": list_records(client, args.domain, rr=args.rr, record_type=getattr(args, "type", None)) elif args.action == "add": add_record(client, args.domain, args.rr, args.type, args.value, args.ttl) elif args.action == "update": update_record(client, args.record_id, args.rr, args.type, args.value, args.ttl) elif args.action == "delete": delete_record(client, args.record_id) elif args.action == "enable": set_record_status(client, args.record_id, "Enable") elif args.action == "disable": set_record_status(client, args.record_id, "Disable") if __name__ == "__main__": main()