Skip to main content

Using Python to migrate DigitalOcean domains to Terraform managed

·2 mins

I was thinking about migrating some cloud services into Terraform, but we seemed to have too many domains. So I wrote a small python script that takes a domain, then using doctl (the DigitalOcean command line) it extracts all current records. After that it outputs the file into tf and also gives a import command to import the current state in Terraform state. This assumes you already installed and configured both doctl and Terraform with the Digitalocean cloud provider.

The usage is simple:

$ python3 doctl-domain-to-terraform.py techwolf12.nl 

This would then give an output similar to:

tf file:

resource "digitalocean_domain" "techwolf12_nl" {
    name = "techwolf12.nl"
}

resource "digitalocean_record" "techwolf12_nl_NS_" {
    domain = digitalocean_domain.techwolf12_nl.id
    type = "NS"
    name = "@"
    value = "ns1.digitalocean.com."
}
resource "digitalocean_record" "techwolf12_nl_NS__" {
    domain = digitalocean_domain.techwolf12_nl.id
    type = "NS"
    name = "@"
    value = "ns2.digitalocean.com."
}

Import commands:

terraform import digitalocean_domain.techwolf12_nl techwolf12.nl
terraform import digitalocean_record.techwolf12_nl_NS_ techwolf12.nl,1234567
terraform import digitalocean_record.techwolf12_nl_NS__ techwolf12.nl,7654321

You can import this as TF file and then use the import commands to import the current state.

The code is available here, save it as a file and run it as instructed above:

"""Doctl Domain to Terraform

Made by Techwolf12, more info: https://techwolf12.nl/blog/using-python-migrate-digitalocean-domains-terraform-managed
"""

import subprocess
import sys
import json

def get_unique_resource(resource_string):
    if resource_string in list_of_record_resource_strings:
        return get_unique_resource(f'{resource_string}_')
    else:
        list_of_record_resource_strings.append(resource_string)
        return resource_string


if len(sys.argv) < 2:
    print("Need domain as argument")
    quit(1)

domain = sys.argv[1]
output = subprocess.run(['doctl', 'compute', 'domain', 'records', 'list', '-o', 'json', domain.encode('utf-8')], stdout=subprocess.PIPE).stdout.decode('utf-8')
records = json.loads(output)
domain_resource = str(domain).replace('.', '_')
import_commands = ""
list_of_record_resource_strings = list()

print("tf file:\n\n")
print(f'''
resource "digitalocean_domain" "{domain_resource:s}" {{
    name = "{domain:s}"
}}
''')
import_commands += f'terraform import digitalocean_domain.{domain_resource:s} {domain:s}\n'

supported_records = ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'TXT', 'SRV']

for record in records:
    if record.get('type') in supported_records:
        record_name = record.get("name").replace(".", "_") if record.get("name") != "@" else ""
        resource = get_unique_resource(f'{domain_resource:s}_{record.get("type")}_{record_name}')
        record_value = record.get("data").replace("\"","\\\"")
        if record.get('type') in ['CNAME', 'MX', 'NS']:
            record_value += "."
        print(f'resource "digitalocean_record" "{resource}" {{')
        print(f'    domain = digitalocean_domain.{domain_resource:s}.id')
        print(f'    type = "{record.get("type")}"')
        if record.get('type') == 'MX':
            print(f'    priority = "{record.get("priority")}"')
        print(f'    name = "{record.get("name")}"')
        print(f'    value = "{record_value}"')
        print("}\n")
        import_commands += f'terraform import digitalocean_record.{resource} {domain},{record.get("id")}\n'

print("\n\nImport commands:\n\n")
print(import_commands)

Onto next time! :)