Package: hetzner-dns-tools
hetzner-dns-tools"A simple Hetzner DNS API client for Python and Bash."This library makes it easier to work with Hetzner'sDNS API, namely Zones and Records.Project Status: All basic CRUD functionality is complete. Tests still need to be written. Bulk record CREATE options not (yet) supported.To be specific,hetzner-dns-toolsmakes it easier to manage your zones/records by name instead of having to get the ID first (although you can do that as well). Also, it allows you to retrieveonlythe IDs if needed, without having to manually parse the JSON first.Limitations:hetzner-dns-toolsdoes not currently do bulk operations other than bulk record deletion, and it does not work with query params (it would be easy to add if you are so inclined). Pull requests and forks are welcomed! :)These tools are made with Python and are designed to be used in Bash or Python.All examples in this README assume you are in the root directory of this project when running commands.Table of ContentsSetupSetting ParametersHow to Use This LibraryProject StructureConverting Results to Human-Readable OutputUsage GuideZoneszone_listzone_createzone_getzone_deleteRecordsrecord_listrecord_createrecord_getrecord_deleteSetupEnsure you have aHetzner DNS API token.Install thehetzner-dns-toolspackage:Via pip (recommended):pip install hetzner-dns-toolsVia GitHub:Clone thehetzner-dns-toolsrepo:git clone https://github.com/arcanemachine/hetzner-dns-toolsNavigate to the root folder of the project.Ensure the python3buildmodule is installed:python3 -m pip install buildBuild the project:python3 -m buildInstall the package:python3 -m pip install .Basic usage example:HETZNER_DNS_TOKEN=your-hetzner-dns-token hetzner-dns-tools zone list(Note: To prevent sensitive data from being saved in your Bash history, ensure that this command begins with a space, or set theenvironment variable somewhere else.Run In DockerInstallDocker Engine.Start the container and view the logs while the project builds:docker run --name hetzner-dns-tools -dt arcanemachine/hetzner-dns-tools && docker logs -f hetzner-dns-toolsAfter the project finishes building* pressCtrl + Cto exit the logs, and open a bash shell in the same container:docker exec -it hetzner-dns-tools bash*You can exit the logs before the build completes. However,hetzner-dns-toolswill not be available from the command line until the build is complete.From this shell, you can runhetzner-dns-toolsor openpython3andimport hetzner_dns_tools.When you are finished, you can stop and destroy the container withdocker stop -t 1 hetzner-dns-toolsanddocker rm hetzner-dns-tools.Note:This library allows indirect lookups to be performed by domain name or other parameters, which will result in multiple requests being issued. To decrease the run time, use zone IDs and record IDs whenever possible.Setting ParametersThere are two methods of setting parameters using this library:Bash environment variablese.g.HETZNER_DNS_TOKEN=your-hetzner-token hetzner-dns-tools zone list*Python argumentse.g.zone_list(hetzner_dns_token='your-hetzner-token')*If you are using this library via Bash, you may want to look into something likedirenvin order to prevent your DNS token from leaking into your~/.bash history.Any arguments used when calling a Python function will override the values of any environment variables.Please note that thenameparameter is used in Hetzner's DNS API calls (specifically inzone_createandrecord_create), while thezone_nameparameter is only used inhetzner-dns-tools.How to Use This LibraryAll API calls require ahetzner_dns_tokenparameter to be set.In BashSimply execute the file you want to run. For example:hetzner-dns-tools zone list(Ensure that you setHETZNER_DNS_TOKENenvironment variable before every command. Readthis sectionto learn more about setting environment variables.) Data can be added to the command by setting environment variables, e.g.NAME=your-domain.com hetzner-dns-tools zone create.To get the Python docstring (ie. help file) for a function, set the environment variableSHOW_HELPto a truthy value, e.g.SHOW_HELP=1 hetzner-dns-tools zone getTo know whether a script executed successfully or not, runecho $?after running a command. If the value of$?is0, the script executed successfully. If the value of$?is1, the script exited with an error.Most errors that occur in the Python code (e.g. if you forget to set an environment variable) will raise an exception and print a stack trace in the console. Other errors will begin withError:and contain a description of the error.In Pythonfrom hetzner_dns_tools.zone_list import zone_list

# THIS EXAMPLE WILL NOT WORK IF YOU HAVEN'T CREATED ANY HETZNER DNS ZONES

# get zone list
dns_zones = zone_list()['zones']

# parse ID from first zone
your_zone_id = dns_zones[0]['id']

print(your_zone_id)Project StructureThese tools are namespaced by feature into modules:zone_create, zone_get, and zone_deleterecord_create, record_get, and record_deletee.g. Runninghetzner-dns-tools zone listin Bash will list all available DNS zones. (Make sure to pass yourHETZNER_DNS_TOKENas an environment variable)Converting Results to Human-Readable OutputThe default output is nearly impossible for humans to read. Here's how to format it so it looks better:In BashThis method requires you to havenpminstalled:Install the npm packagejsonglobally:npm i -g jsonPipe the output of a command to the newly-installedjsonpackage:hetzner-dns-tools zone listjsonIf you know of a better method (particularly one that doesn't requirenpmto be installed), pleasesubmit a ticket.In Pythonimport json
from hetzner_dns_tools.zone_list import zone_list

# get zone list
dns_zones = zone_list()['zones']

# make it readable
readable_dns_zones = json.dumps(dns_zones, indent=2)

print(readable_dns_zones)Usage GuideZone Functionszone_listzone_createzone_getzone_deleteRecord Functionsrecord_listrecord_createrecord_getrecord_deleteThis section assumes that you have exported theHETZNER_DNS_TOKENenvironment variable before running any Bash commands. Readthe section on setting Bash environment variablesif you don't know how to do this.)In Bash, results will be returned as a condensed JSON-formatted string, except when string values (e.g. zone IDs) are requested, or when DELETE actions are performed (will return 'OK' if successful).In Python, results will be returned as a dictionary, except when string values (e.g. zone IDs) are requested, or when DELETE actions are performed (will return 'OK'  if successful.Zoneszone_listGet list of all zones.(Hetzner Docs API - Get All Zones)In Bashhetzner-dns-tools zone listIn Pythonfrom hetzner_dns_tools.zone_list import zone_list

your_zones = zone_list()

print(your_zones)zone_createCreate a new zone.(Hetzner DNS API Docs - Create Zone)Required Parameters:name/zone_name(nameandzone_nameare interchangeable)Optional Parameters:ttlNOTE:zone_createandzone_deleteallow thenameandzone_nameparameters (or theNAMEandZONE_NAMEenvironment variables) to be used interchangeably. Note that thenameparameter is used in Hetzner's API, butzone_nameis commonly used in this library, so I allow both to be used to reduce the cognitive burden of having to switch from one to the other.In BashTo return all data for the zone:NAME=your-domain.com hetzner-dns-tools zone createorZONE_NAME=your-domain.com hetzner-dns-tools zone createTo return just the zone ID:NAME=your-domain.com ID_ONLY=1 hetzner-dns-tools zone createTo create a new zone with a custom TTL (default: 86400):NAME=your-domain.com TTL=57600 hetzner-dns-tools zone createIn PythonTo get all data for the new zone:from hetzner_dns_tools.zone_create import zone_create

# create a new zone and return all zone data
new_zone = zone_create(hetzner_dns_token='your-token',
                       name='your-domain.com')  # can also use zone_name

# print the name of the new zone
print(new_zone['zone']['name'])  # 'your-domain.com'To return just the ID for the new zone:from hetzner_dns_tools.zone_create import zone_create

# create a new zone and return just the zone_id
new_zone_id = zone_create(hetzner_dns_token='your-token',
                          zone_name='your-domain.com',  # can also use name
                          id_only=True)

# print the ID of the new zone
print(new_zone_id)To create a new zone with a custom TTL (default: 86400):from hetzner_dns_tools.zone_create import zone_create

# create a new zone and return all zone data
new_zone = zone_create(hetzner_dns_token='your-token',
                       name='your-domain.com',  # can also use zone_name
                       ttl=57600)

# print the TTL of the new zone
print(new_zone['zone']['ttl'])  # 57600zone_getGet info about an existing zone.(Hetzner DNS API Docs - Get Zone)Required Parameters:One of:zone_idorzone_nameOptional Parameters:id_onlyIn BashTo return all data for the zone by using the zone ID:ZONE_ID=your-zone-id hetzner-dns-tools zone getTo return all data for the zone by using the zone's domain name:ZONE_NAME=your-domain.com hetzner-dns-tools zone getTo return just the zone ID by using the zone's domain name:ZONE_NAME=your-domain.com ID_ONLY=1 hetzner-dns-tools zone getIn PythonTo return all data for the zone by using the zone ID:from hetzner_dns_tools.zone_get import zone_get

zone = zone_get(hetzner_dns_token='your-token',
                zone_id='your-zone-id')

# print the name of the zone
print(zone['zone']['name'])To return all data for the zone by using the zone's domain name:from hetzner_dns_tools.zone_get import zone_get

zone = zone_get(hetzner_dns_token='your-token',
                zone_name='your-domain.com')

# print the ID of the zone
print(zone['zone']['id'])To return just the zone ID by using the zone's domain name:from hetzner_dns_tools.zone_get import zone_get

zone_id = zone_get(hetzner_dns_token='your-token',
                   zone_name='your-domain.com',
                   id_only=True)

# print the ID of the zone
print(zone_id)zone_updateTo update a zone, usezone_deleteto delete a zone, and then usezone_createto create a new one. (I had some issues getting the function to work with Hetzner's backend (it wouldn't allow any changes to be made), plus the ability to delete and create records effectively makes the update function redundant. If you want to try to implement the function yourself, there is azone_update.pyfile in theexperimental-functionsbranch that may be of some help.zone_deleteDelete an existing zone.(Hetzner DNS API Docs - Delete Zone)Required Parameters:One of:zone_idorzone_name/nameZones can be deleted directly using azone_id, or can be done indirectly by using any of theOptional Parametersas a lookup.Successful delete operations will return the string 'OK', and unsuccessful delete operations will raise aValueErrorexception.NOTE:zone_createandzone_deleteallow thenameandzone_nameparameters (or theNAMEandZONE_NAMEenvironment variables) to be used interchangeably. Note that thenameparameter is used in Hetzner's API, butzone_nameis commonly used in this library, so I allow both to be used to reduce the cognitive burden of having to switch from one to the other.In BashTo delete a zone by its zone ID:ZONE_ID=your-zone-id hetzner-dns-tools zone deleteTo delete a zone by its zone (ie. domain) name:ZONE_NAME=your-domain.com hetzner-dns-tools zone deleteorNAME=your-domain.com hetzner-dns-tools zone deleteIn PythonTo delete a zone by its zone ID:from hetzner_dns_tools.zone_delete import zone_delete

zone_delete(hetzner_dns_token='your-token',
            zone_id='your-zone-id')To delete a zone by its zone (ie. domain) name:from hetzner_dns_tools.zone_delete import zone_delete

zone_delete(hetzner_dns_token='your-token',
            zone_name='your-domain.com')  # can also use 'name'Recordsrecord_listGet list of all records.(Hetzner DNS API Docs - Get All Records)Required Parameters:One of:zone_idorzone_nameIn BashTo return all data for all zones:hetzner-dns-tools record listTo return all data for a single zone, by zone ID:ZONE_ID=your-zone-id hetzner-dns-tools record listTo return all data for a single zone, by zone (ie. domain) name:ZONE_NAME=your-domain.com hetzner-dns-tools record listIn PythonTo return all data for all zones:from hetzner_dns_tools.record_list import record_list

records = record_list(hetzner_dns_token='your-token')

print(records)To return all data for a single zone, by zone ID:from hetzner_dns_tools.record_list import record_list

records = record_list(hetzner_dns_token='your-token',
                      zone_id='your-zone-id')

print(records)To return all data for a single zone, by zone (ie. domain) name:from hetzner_dns_tools.record_list import record_list

records = record_list(hetzner_dns_token='your-token',
                      zone_name='your-domain.com')

print(records)record_createCreate a new record.(Hetzner DNS API Docs - Create Record)Required Parameters:hetzner_dns_token,record_type,value,zone_idOptional Parameters:zone_name,name*,ttl,id_only*Ifnameis not specified, the root value@will be used (except for MX records).To get the ID of the zone you want to create the record in, you can usezone_nameto do an indirect lookup an obtain thezone_id. Note that doing this will result in an additional request being made.Note:Hetzner's DNS API requires a thetypeparameter to specify the type of record (e.g. A, AAAA, CNAME, MX, etc.). Because the wordtypeis a reserved keyword in Python, therecordfunctions all use therecord_typeparameter instead. When using environment variables, eitherTYPEorRECORD_TYPEmay be used interchangeably.In BashTo create anArecord for zone IDyour-zone-idwith namewwwand value1.1.1.1, and return all record data:ZONE_ID=your-zone-id TYPE=A NAME=www VALUE=1.1.1.1 hetzner-dns-tools record createTo return just the record ID after creating the record:ZONE_ID=your-zone-id RECORD_TYPE=A NAME=www VALUE=1.1.1.1 ID_ONLY=1 hetzner-dns-tools record createTo create a new record with a custom TTL (default is86400), useTTL:ZONE_ID=your-zone-id TYPE=A NAME=www VALUE=1.1.1.1 TTL=57600 hetzner-dns-tools record createTo create a MX record with a target ofyour-domain.comand a priority of10, both values must be entered as such in theVALUEfield:ZONE_ID=your-zone-id TYPE=MX VALUE="10 your-mail-server.com" hetzner-dns-tools record createTo create a SRV record with a priority of1, a weight of2, at port3for a targetyour-server.com, all values must be entered in this order in theVALUEfield:ZONE_ID=your-zone-id TYPE=SRV VALUE="1 2 3 your-server.com" hetzner-dns-tools record createIn PythonTo create anArecord for zone IDyour-zone-idwith namewwwand value1.1.1.1, and return all record data:from hetzner_dns_tools.record_create import record_create

# create a new record and return all record data
new_record = record_create(hetzner_dns_token='your-token',
                           zone_id='your-zone-id',
                           record_type='A',
                           name='www',
                           value='1.1.1.1')

# print the record's 'created' value
print(new_record['record']['created'])To return just the record ID after creating the record:from hetzner_dns_tools.record_create import record_create

# create a new record and return all record data
new_record_id = record_create(hetzner_dns_token='your-token',
                              zone_id='your-zone-id',
                              record_type='A',
                              name='www',
                              value='1.1.1.1',
                              id_only=True)

# print the new record's ID
print(new_record_id)To create a new MX record with serveryour-mail-server.comand priority10:from hetzner_dns_tools.record_create import record_create

# create a new record and return all record data
new_record = record_create(hetzner_dns_token='your-token',
                           zone_id='your-zone-id',
                           record_type='MX',
                           value='10 your.mail-server.com')To create a SRV record with a priority of1, a weight of2, at port3for a targetyour-server.com:from hetzner_dns_tools.record_create import record_create

# create a new record and return all record data
new_record = record_create(hetzner_dns_token='your-token',
                           zone_id='your-zone-id',
                           record_type='SRV',
                           value='1 2 3 your.server.com')record_getGet info about an existing record.(Hetzner DNS API Docs - Get Record)Required* Parameters:One of:record_idorzone_idorzone_nameOptional Parameters:Filters:record_type,name,value,ttlFormats:id_onlyOptions:first_record_only,allow_multiple_records,search_all_zones**If thesearch_all_zonesparameter is given a truthy value, then you do not need to include any of theRequired Parameters, as their purpose is to ensure that records are only returned for a single zone.Note:This function will raise an exception if multiple records are returned, *unless* thefirst_record_only*or*allow_multiple_recordsparameters are truthy.OptionsThese parameters can be given truthy values to enable them:allow_multiple_records- If multiple records are returned, return all of them.first_record_only- Return only the first record found. (There is no guarantee of any ordering.)search_all_zones- Allow records to be returned from all zones. No required parameters are needed when using this option.id_only- Returns only the ID of the given record. If this argument andallow_multiple_recordsare both truthy, a list of record IDs will be returned.In BashTo return all data for single record via the record's ID:RECORD_ID=your-record-id hetzner-dns-tools record getTo return all MX records for a zone by using a zone ID as a lookup:ZONE_ID=your-zone-id TYPE=MX ALLOW_MULTIPLE_RECORDS=1 hetzner-dns-tools record getTo return a zone's A record with a name of 'www' by using a zone (ie. domain) name as a lookup:ZONE_NAME=your-domain.com TYPE=A NAME=www hetzner-dns-tools record getTo return all record IDs for a zone by using a zone ID as a lookup:ZONE_ID=your-zone-id ALLOW_MULTIPLE_RECORDS=1 ID_ONLY=1 hetzner-dns-tools record getTo return all A records from all zones with a name of '@' (root):TYPE=A NAME="@" SEARCH_ALL_ZONES=1 ALLOW_MULTIPLE_RECORDS=1 hetzner-dns-tools record getTo return the first returned A record with a value of1.2.3.4and a TTL of57600by using a zone (ie. domain) name as a lookup:ZONE_NAME=your-domain.com TYPE=A VALUE=1.2.3.4 TTL=57600 FIRST_RECORD_ONLY=1 hetzner-dns-tools record getIn PythonTo return all data for single record via the record's ID:from hetzner_dns_tools.record_get import record_get

record = record_get(hetzner_dns_token='your-token',
                    record_id='your-record-id')To return all MX records for a zone by using a zone ID as a lookup:from hetzner_dns_tools.record_get import record_get

records = record_get(hetzner_dns_token='your-token',
                     zone_id='your-zone-id',
                     record_type='MX',
                     allow_multiple_records=True)To return a zone's A record with a name of 'www' by using a zone (ie. domain) name as a lookup:from hetzner_dns_tools.record_get import record_get

record = record_get(hetzner_dns_token='your-token',
                    zone_id='your-zone-id',
                    record_type='A',
                    name='www')To return all MX records for a zone by using a zone ID as a lookup:from hetzner_dns_tools.record_get import record_get

records = record_get(hetzner_dns_token='your-token',
                     zone_id='your-zone-id',
                     record_type='MX',
                     allow_multiple_records=True)To return all record IDs for a zone by using a zone ID as a lookup:from hetzner_dns_tools.record_get import record_get

# this value will contain a list of zone IDs
record_ids = record_get(hetzner_dns_token='your-token',
                        zone_id='your-zone-id',
                        allow_multiple_records=True,
                        id_only=True)To return all record IDs for a zone by using a zone ID as a lookup:from hetzner_dns_tools.record_get import record_get

# this value will contain a list of zone IDs
record_ids = record_get(hetzner_dns_token='your-token',
                        zone_id='your-zone-id',
                        allow_multiple_records=True,
                        id_only=True)To return all A records from all zones with a name of '@' (root):from hetzner_dns_tools.record_get import record_get

# this value will contain a list of zone IDs
record_ids = record_get(hetzner_dns_token='your-token',
                        search_all_zones=True,
                        record_type='A',
                        name='@',
                        allow_multiple_records=True)To return the first record with a value of1.2.3.4by using a zone (ie. domain) name as a lookup:from hetzner_dns_tools.record_get import record_get

# this value will contain a list of zone IDs
record_ids = record_get(hetzner_dns_token='your-token',
                        zone_name='your-domain.com',
                        value='1.2.3.4',
                        first_record_only=True)record_updateAs with thezonemodules, you can userecord_deleteandrecord_createto update a record. This library does not currently have a nativerecord_updatemodule.record_deleteDelete an existing record.(Hetzner DNS API Docs - Delete Record)Required* Parameters:One of:record_idorzone_idorzone_nameOptional Parameters:Filters:record_type,name,value(but notttl*)Options:delete_multiple_records,first_record_only,search_all_zones**Records can be deleted directly using arecord_id, or can be done indirectly by using any of theOptional Parametersas a lookup.Successful delete operations will return the string 'OK', and unsuccessful delete operations will raise aValueErrorexception.*Due to howrecord_listreturns its results,ttlis not an available option for filtering records**If thesearch_all_zonesparameter is given a truthy value, then you do not need to include any of theRequired Parameters, as their purpose is to ensure that records are only returned for a single zone.Note:This function will raise an exception if multiple records are returned, *unless* thefirst_record_only*or*allow_multiple_recordsparameters are truthy.OptionsThese parameters can be given truthy values to enable them:delete_multiple_records- Delete all matching records, even if there is more than one record returned.first_record_only- Delete only the first record returned. (There is no guarantee of any ordering.)search_all_zones- Allow records to be returned from all zones. None of "required" parameters are needed when using this option.In BashTo delete a record by using its record ID:RECORD_ID=your-record-id hetzner-dns-tools record deleteTo delete a zone's A record with a name of 'www' by using a zone (ie. domain) name as a lookup:ZONE_NAME=your-domain.com TYPE=A NAME=www hetzner-dns-tools record deleteTo delete all MX records for a zone by using a zone ID as a lookup:ZONE_ID=your-zone-id TYPE=MX DELETE_MULTIPLE_RECORDS=1 hetzner-dns-tools record deleteTo delete all CNAME records from all zones:TYPE=CNAME SEARCH_ALL_ZONES=1 DELETE_MULTIPLE_RECORDS=1 hetzner-dns-tools record deleteTo delete the first returned A record with a value of1.2.3.4by using a zone (ie. domain) name as a lookup:ZONE_NAME=your-domain.com TYPE=A VALUE=1.2.3.4 FIRST_RECORD_ONLY=1 hetzner-dns-tools record deleteIn PythonTo delete a record by using the record ID:from hetzner_dns_tools.record_delete import record_delete

record_delete(hetzner_dns_token='your-token',
              record_id='your-record-id')To delete a zone's A record with a name of 'www' by using a zone (ie. domain) name as a lookup:from hetzner_dns_tools.record_delete import record_delete

record_delete(hetzner_dns_token='your-token',
              zone_name='your-domain.com',
              record_type='A',
              name='www')To delete all MX records for a zone by using a zone ID as a lookup:from hetzner_dns_tools.record_delete import record_delete

record_delete(hetzner_dns_token='your-token',
              zone_id='your-zone-id',
              record_type='MX',
              delete_multiple_records=True)To delete all A records from all zones with a name of '@' (root):from hetzner_dns_tools.record_delete import record_delete

# will raise ValueError return 'OK' if delete operation was successful
record_delete(hetzner_dns_token='your-token',
              record_type='A',
              name='@',
              search_all_zones=True,
              delete_multiple_records=True)To delete the first returned A record with a value of1.2.3.4by using a zone (ie. domain) name as a lookup:from hetzner_dns_tools.record_delete import record_delete

# will raise ValueError return 'OK' if delete operation was successful
record_delete(hetzner_dns_token='your-token',
              zone_name='your-domain.com',
              record_type='A',
              value='1.2.3.4',
              first_record_only=True)(c) 2022 arcanemachine. Freely distributed under the terms of theMIT Licence.