From 50ade7bce199fce08fdd785d04cbb07572b52e8b Mon Sep 17 00:00:00 2001
From: Tobias Genannt <t.genannt@scanplus.de>
Date: Wed, 5 Feb 2020 15:31:01 +0100
Subject: [PATCH] Add function to load YAML files

This commit starts to remove some code redundancy from the startup
scripts for easier maintenance.
---
 startup_scripts/000_users.py                  | 57 ++++++------
 startup_scripts/010_groups.py                 | 56 ++++++------
 startup_scripts/020_custom_fields.py          | 63 ++++++-------
 startup_scripts/030_regions.py                | 37 ++++----
 startup_scripts/040_sites.py                  | 59 ++++++------
 startup_scripts/050_manufacturers.py          | 21 ++---
 startup_scripts/060_device_types.py           | 73 +++++++--------
 startup_scripts/070_rack_roles.py             | 31 +++----
 startup_scripts/075_rack_groups.py            | 36 ++++----
 startup_scripts/080_racks.py                  | 75 ++++++++--------
 startup_scripts/090_device_roles.py           | 32 +++----
 startup_scripts/100_platforms.py              | 38 ++++----
 startup_scripts/110_tenant_groups.py          | 21 ++---
 startup_scripts/120_tenants.py                | 58 ++++++------
 startup_scripts/130_devices.py                | 86 +++++++++---------
 startup_scripts/140_cluster_types.py          | 21 ++---
 startup_scripts/150_rirs.py                   | 21 ++---
 startup_scripts/160_aggregates.py             | 58 ++++++------
 startup_scripts/170_clusters.py               | 74 +++++++--------
 startup_scripts/180_vrfs.py                   | 58 ++++++------
 startup_scripts/190_prefix_vlan_roles.py      | 21 ++---
 startup_scripts/200_vlan_groups.py            | 58 ++++++------
 startup_scripts/210_vlans.py                  | 66 +++++++-------
 startup_scripts/220_prefixes.py               | 72 +++++++--------
 startup_scripts/230_virtual_machines.py       | 76 ++++++++--------
 .../240_virtualization_interfaces.py          | 56 ++++++------
 startup_scripts/250_dcim_interfaces.py        | 56 ++++++------
 startup_scripts/260_ip_addresses.py           | 89 +++++++++----------
 .../startup_script_utils/__init__.py          |  1 +
 .../startup_script_utils/load_yaml.py         | 10 +++
 30 files changed, 669 insertions(+), 811 deletions(-)
 create mode 100644 startup_scripts/startup_script_utils/__init__.py
 create mode 100644 startup_scripts/startup_script_utils/load_yaml.py

diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py
index 6962df0..534a793 100644
--- a/startup_scripts/000_users.py
+++ b/startup_scripts/000_users.py
@@ -1,42 +1,37 @@
 from django.contrib.auth.models import Permission, Group, User
 from users.models import Token
 
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/users.yml')
-if not file.is_file():
+users = load_yaml('/opt/netbox/initializers/users.yml')
+
+if users is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  users = yaml.load(stream)
+for username, user_details in users.items():
+  if not User.objects.filter(username=username):
+    user = User.objects.create_user(
+      username = username,
+      password = user_details.get('password', 0) or User.objects.make_random_password)
 
-  if users is not None:
-    for username, user_details in users.items():
-      if not User.objects.filter(username=username):
-        user = User.objects.create_user(
-          username = username,
-          password = user_details.get('password', 0) or User.objects.make_random_password)
+    print("πŸ‘€ Created user",username)
 
-        print("πŸ‘€ Created user",username)
+    if user_details.get('api_token', 0):
+      Token.objects.create(user=user, key=user_details['api_token'])
 
-        if user_details.get('api_token', 0):
-          Token.objects.create(user=user, key=user_details['api_token'])
+    yaml_permissions = user_details.get('permissions', [])
+    if yaml_permissions:
+      subject = user.user_permissions
+      subject.clear()
+      for yaml_permission in yaml_permissions:
+        if '*' in yaml_permission:
+          permission_filter = '^' + yaml_permission.replace('*','.*') + '$'
+          permissions = Permission.objects.filter(codename__iregex=permission_filter)
+          print("  ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'")
+        else:
+          permissions = Permission.objects.filter(codename=yaml_permission)
+          print("  ⚿ Granting permission", yaml_permission)
 
-        yaml_permissions = user_details.get('permissions', [])
-        if yaml_permissions:
-          subject = user.user_permissions
-          subject.clear()
-          for yaml_permission in yaml_permissions:
-            if '*' in yaml_permission:
-              permission_filter = '^' + yaml_permission.replace('*','.*') + '$'
-              permissions = Permission.objects.filter(codename__iregex=permission_filter)
-              print("  ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'")
-            else:
-              permissions = Permission.objects.filter(codename=yaml_permission)
-              print("  ⚿ Granting permission", yaml_permission)
-            
-            for permission in permissions:
-              subject.add(permission)
+        for permission in permissions:
+          subject.add(permission)
diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py
index 08fb4bf..bad2a10 100644
--- a/startup_scripts/010_groups.py
+++ b/startup_scripts/010_groups.py
@@ -1,41 +1,35 @@
 from django.contrib.auth.models import Permission, Group, User
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/groups.yml')
-if not file.is_file():
+groups = load_yaml('/opt/netbox/initializers/groups.yml')
+if groups is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  groups = yaml.load(stream)
+for groupname, group_details in groups.items():
+  group, created = Group.objects.get_or_create(name=groupname)
 
-  if groups is not None:
-    for groupname, group_details in groups.items():
-      group, created = Group.objects.get_or_create(name=groupname)
+  if created:
+    print("πŸ‘₯ Created group", groupname)
 
-      if created:
-        print("πŸ‘₯ Created group", groupname)
+  for username in group_details.get('users', []):
+    user = User.objects.get(username=username)
 
-      for username in group_details.get('users', []):
-        user = User.objects.get(username=username)
+    if user:
+      user.groups.add(group)
 
-        if user:
-          user.groups.add(group)
+  yaml_permissions = group_details.get('permissions', [])
+  if yaml_permissions:
+    subject = group.permissions
+    subject.clear()
+    for yaml_permission in yaml_permissions:
+      if '*' in yaml_permission:
+        permission_filter = '^' + yaml_permission.replace('*','.*') + '$'
+        permissions = Permission.objects.filter(codename__iregex=permission_filter)
+        print("  ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'")
+      else:
+        permissions = Permission.objects.filter(codename=yaml_permission)
+        print("  ⚿ Granting permission", yaml_permission)
 
-      yaml_permissions = group_details.get('permissions', [])
-      if yaml_permissions:
-        subject = group.permissions
-        subject.clear()
-        for yaml_permission in yaml_permissions:
-          if '*' in yaml_permission:
-            permission_filter = '^' + yaml_permission.replace('*','.*') + '$'
-            permissions = Permission.objects.filter(codename__iregex=permission_filter)
-            print("  ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'")
-          else:
-            permissions = Permission.objects.filter(codename=yaml_permission)
-            print("  ⚿ Granting permission", yaml_permission)
-
-          for permission in permissions:
-            subject.add(permission)
+      for permission in permissions:
+        subject.add(permission)
diff --git a/startup_scripts/020_custom_fields.py b/startup_scripts/020_custom_fields.py
index 2f6ba72..2cb48a0 100644
--- a/startup_scripts/020_custom_fields.py
+++ b/startup_scripts/020_custom_fields.py
@@ -1,7 +1,6 @@
 from extras.models import CustomField, CustomFieldChoice
 
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
 def get_class_for_class_path(class_path):
@@ -13,47 +12,43 @@ def get_class_for_class_path(class_path):
   clazz = getattr(module, class_name)
   return ContentType.objects.get_for_model(clazz)
 
-file = Path('/opt/netbox/initializers/custom_fields.yml')
-if not file.is_file():
+customfields = load_yaml('/opt/netbox/initializers/custom_fields.yml')
+
+if customfields is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  customfields = yaml.load(stream)
+for cf_name, cf_details in customfields.items():
+  custom_field, created = CustomField.objects.get_or_create(name = cf_name)
 
-  if customfields is not None:
-    for cf_name, cf_details in customfields.items():
-      custom_field, created = CustomField.objects.get_or_create(name = cf_name)
+  if created:
+    if cf_details.get('default', 0):
+      custom_field.default = cf_details['default']
 
-      if created:
-        if cf_details.get('default', 0):
-          custom_field.default = cf_details['default']
+    if cf_details.get('description', 0):
+      custom_field.description = cf_details['description']
 
-        if cf_details.get('description', 0):
-          custom_field.description = cf_details['description']
+    if cf_details.get('label', 0):
+      custom_field.label = cf_details['label']
 
-        if cf_details.get('label', 0):
-          custom_field.label = cf_details['label']
+    for object_type in cf_details.get('on_objects', []):
+      custom_field.obj_type.add(get_class_for_class_path(object_type))
 
-        for object_type in cf_details.get('on_objects', []):
-          custom_field.obj_type.add(get_class_for_class_path(object_type))
+    if cf_details.get('required', 0):
+      custom_field.required = cf_details['required']
 
-        if cf_details.get('required', 0):
-          custom_field.required = cf_details['required']
+    if cf_details.get('type', 0):
+      custom_field.type = cf_details['type']
 
-        if cf_details.get('type', 0):
-          custom_field.type = cf_details['type']
+    if cf_details.get('weight', 0):
+      custom_field.weight = cf_details['weight']
 
-        if cf_details.get('weight', 0):
-          custom_field.weight = cf_details['weight']
+    custom_field.save()
 
-        custom_field.save()
+    for idx, choice_details in enumerate(cf_details.get('choices', [])):
+      choice, _ = CustomFieldChoice.objects.get_or_create(
+        field=custom_field,
+        value=choice_details['value'],
+        defaults={'weight': idx * 10}
+      )
 
-        for idx, choice_details in enumerate(cf_details.get('choices', [])):
-          choice, _ = CustomFieldChoice.objects.get_or_create(
-            field=custom_field,
-            value=choice_details['value'],
-            defaults={'weight': idx * 10}
-          )
-
-        print("πŸ”§ Created custom field", cf_name)
+    print("πŸ”§ Created custom field", cf_name)
diff --git a/startup_scripts/030_regions.py b/startup_scripts/030_regions.py
index d9e7bdf..0b61d89 100644
--- a/startup_scripts/030_regions.py
+++ b/startup_scripts/030_regions.py
@@ -1,31 +1,26 @@
 from dcim.models import Region
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/regions.yml')
-if not file.is_file():
+regions = load_yaml('/opt/netbox/initializers/regions.yml')
+
+if regions is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  regions = yaml.load(stream)
+optional_assocs = {
+  'parent': (Region, 'name')
+}
 
-  optional_assocs = {
-    'parent': (Region, 'name')
-  }
+for params in regions:
 
-  if regions is not None:
-    for params in regions:
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  region, created = Region.objects.get_or_create(**params)
 
-      region, created = Region.objects.get_or_create(**params)
-
-      if created:
-        print("🌐 Created region", region.name)
+  if created:
+    print("🌐 Created region", region.name)
diff --git a/startup_scripts/040_sites.py b/startup_scripts/040_sites.py
index 27be053..828a86b 100644
--- a/startup_scripts/040_sites.py
+++ b/startup_scripts/040_sites.py
@@ -1,46 +1,41 @@
 from dcim.models import Region, Site
 from extras.models import CustomField, CustomFieldValue
 from tenancy.models import Tenant
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/sites.yml')
-if not file.is_file():
+sites = load_yaml('/opt/netbox/initializers/sites.yml')
+
+if sites is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  sites = yaml.load(stream)
+optional_assocs = {
+  'region': (Region, 'name'),
+  'tenant': (Tenant, 'name')
+}
 
-  optional_assocs = {
-    'region': (Region, 'name'),
-    'tenant': (Tenant, 'name')
-  }
+for params in sites:
+  custom_fields = params.pop('custom_fields', None)
 
-  if sites is not None:
-    for params in sites:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  site, created = Site.objects.get_or_create(**params)
 
-      site, created = Site.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=site,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=site,
-              value=cf_value
-            )
+        site.custom_field_values.add(custom_field_value)
 
-            site.custom_field_values.add(custom_field_value)
-
-        print("πŸ“ Created site", site.name)
+    print("πŸ“ Created site", site.name)
diff --git a/startup_scripts/050_manufacturers.py b/startup_scripts/050_manufacturers.py
index 2f8d850..b9ebb32 100644
--- a/startup_scripts/050_manufacturers.py
+++ b/startup_scripts/050_manufacturers.py
@@ -1,19 +1,14 @@
 from dcim.models import Manufacturer
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/manufacturers.yml')
-if not file.is_file():
+manufacturers = load_yaml('/opt/netbox/initializers/manufacturers.yml')
+
+if manufacturers is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  manufacturers = yaml.load(stream)
+for params in manufacturers:
+  manufacturer, created = Manufacturer.objects.get_or_create(**params)
 
-  if manufacturers is not None:
-    for params in manufacturers:
-      manufacturer, created = Manufacturer.objects.get_or_create(**params)
-
-      if created:
-        print("🏭 Created Manufacturer", manufacturer.name)
+  if created:
+    print("🏭 Created Manufacturer", manufacturer.name)
diff --git a/startup_scripts/060_device_types.py b/startup_scripts/060_device_types.py
index b806cfc..e6cea93 100644
--- a/startup_scripts/060_device_types.py
+++ b/startup_scripts/060_device_types.py
@@ -1,56 +1,51 @@
 from dcim.models import DeviceType, Manufacturer, Region
 from tenancy.models import Tenant
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/device_types.yml')
-if not file.is_file():
+device_types = load_yaml('/opt/netbox/initializers/device_types.yml')
+
+if device_types is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  device_types = yaml.load(stream)
+required_assocs = {
+  'manufacturer': (Manufacturer, 'name')
+}
 
-  required_assocs = {
-    'manufacturer': (Manufacturer, 'name')
-  }
+optional_assocs = {
+  'region': (Region, 'name'),
+  'tenant': (Tenant, 'name')
+}
 
-  optional_assocs = {
-    'region': (Region, 'name'),
-    'tenant': (Tenant, 'name')
-  }
+for params in device_types:
+  custom_fields = params.pop('custom_fields', None)
 
-  if device_types is not None:
-    for params in device_types:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  device_type, created = DeviceType.objects.get_or_create(**params)
 
-      device_type, created = DeviceType.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=device_type,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=device_type,
-              value=cf_value
-            )
+        device_type.custom_field_values.add(custom_field_value)
 
-            device_type.custom_field_values.add(custom_field_value)
-
-        print("πŸ”‘ Created device type", device_type.manufacturer, device_type.model)
+    print("πŸ”‘ Created device type", device_type.manufacturer, device_type.model)
diff --git a/startup_scripts/070_rack_roles.py b/startup_scripts/070_rack_roles.py
index 0f43b7f..98a5be4 100644
--- a/startup_scripts/070_rack_roles.py
+++ b/startup_scripts/070_rack_roles.py
@@ -1,28 +1,23 @@
 from dcim.models import RackRole
-from ruamel.yaml import YAML
 from utilities.forms import COLOR_CHOICES
 
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/rack_roles.yml')
-if not file.is_file():
+rack_roles = load_yaml('/opt/netbox/initializers/rack_roles.yml')
+
+if rack_roles is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  rack_roles = yaml.load(stream)
+for params in rack_roles:
+  if 'color' in params:
+    color = params.pop('color')
 
-  if rack_roles is not None:
-    for params in rack_roles:
-      if 'color' in params:
-        color = params.pop('color')
+    for color_tpl in COLOR_CHOICES:
+      if color in color_tpl:
+        params['color'] = color_tpl[0]
 
-        for color_tpl in COLOR_CHOICES:
-          if color in color_tpl:
-            params['color'] = color_tpl[0]
+  rack_role, created = RackRole.objects.get_or_create(**params)
 
-      rack_role, created = RackRole.objects.get_or_create(**params)
-
-      if created:
-        print("🎨 Created rack role", rack_role.name)
+  if created:
+    print("🎨 Created rack role", rack_role.name)
diff --git a/startup_scripts/075_rack_groups.py b/startup_scripts/075_rack_groups.py
index 7deaa11..dc5ec20 100644
--- a/startup_scripts/075_rack_groups.py
+++ b/startup_scripts/075_rack_groups.py
@@ -1,31 +1,25 @@
 from dcim.models import Site,RackGroup
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/rack_groups.yml')
-if not file.is_file():
+rack_groups = load_yaml('/opt/netbox/initializers/rack_groups.yml')
+
+if rack_groups is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  rack_groups= yaml.load(stream)
+required_assocs = {
+  'site': (Site, 'name')
+}
 
-  required_assocs = {
-    'site': (Site, 'name')
-  }
+for params in rack_groups:
 
-  if rack_groups is not None:
-    for params in rack_groups:
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
-        params[assoc] = model.objects.get(**query)
+  rack_group, created = RackGroup.objects.get_or_create(**params)
 
-      rack_group, created = RackGroup.objects.get_or_create(**params)
-
-      if created:
-        print("🎨 Created rack group", rack_group.name)
+  if created:
+    print("🎨 Created rack group", rack_group.name)
 
diff --git a/startup_scripts/080_racks.py b/startup_scripts/080_racks.py
index ed7713d..279cb2c 100644
--- a/startup_scripts/080_racks.py
+++ b/startup_scripts/080_racks.py
@@ -1,57 +1,52 @@
 from dcim.models import Site, RackRole, Rack, RackGroup
 from tenancy.models import Tenant
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/racks.yml')
-if not file.is_file():
+racks = load_yaml('/opt/netbox/initializers/racks.yml')
+
+if racks is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  racks = yaml.load(stream)
+required_assocs = {
+  'site': (Site, 'name')
+}
 
-  required_assocs = {
-    'site': (Site, 'name')
-  }
+optional_assocs = {
+  'role': (RackRole, 'name'),
+  'tenant': (Tenant, 'name'),
+  'group': (RackGroup, 'name')
+}
 
-  optional_assocs = {
-    'role': (RackRole, 'name'),
-    'tenant': (Tenant, 'name'),
-    'group': (RackGroup, 'name')
-  }
+for params in racks:
+  custom_fields = params.pop('custom_fields', None)
 
-  if racks is not None:
-    for params in racks:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  rack, created = Rack.objects.get_or_create(**params)
 
-      rack, created = Rack.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=rack,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=rack,
-              value=cf_value
-            )
+        rack.custom_field_values.add(custom_field_value)
 
-            rack.custom_field_values.add(custom_field_value)
-
-        print("πŸ”³ Created rack", rack.site, rack.name)
+    print("πŸ”³ Created rack", rack.site, rack.name)
diff --git a/startup_scripts/090_device_roles.py b/startup_scripts/090_device_roles.py
index 4584336..c75253d 100644
--- a/startup_scripts/090_device_roles.py
+++ b/startup_scripts/090_device_roles.py
@@ -1,29 +1,23 @@
 from dcim.models import DeviceRole
-from ruamel.yaml import YAML
 from utilities.forms import COLOR_CHOICES
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/device_roles.yml')
-if not file.is_file():
+device_roles = load_yaml('/opt/netbox/initializers/device_roles.yml')
+
+if device_roles is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml=YAML(typ='safe')
-  device_roles = yaml.load(stream)
+for params in device_roles:
 
-  if device_roles is not None:
-    for params in device_roles:
+  if 'color' in params:
+    color = params.pop('color')
 
-      if 'color' in params:
-        color = params.pop('color')
+    for color_tpl in COLOR_CHOICES:
+      if color in color_tpl:
+        params['color'] = color_tpl[0]
 
-        for color_tpl in COLOR_CHOICES:
-          if color in color_tpl:
-            params['color'] = color_tpl[0]
+  device_role, created = DeviceRole.objects.get_or_create(**params)
 
-      device_role, created = DeviceRole.objects.get_or_create(**params)
-
-      if created:
-        print("🎨 Created device role", device_role.name)
+  if created:
+    print("🎨 Created device role", device_role.name)
diff --git a/startup_scripts/100_platforms.py b/startup_scripts/100_platforms.py
index cb47054..3673230 100644
--- a/startup_scripts/100_platforms.py
+++ b/startup_scripts/100_platforms.py
@@ -1,32 +1,26 @@
 from dcim.models import Manufacturer, Platform
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/platforms.yml')
-if not file.is_file():
+platforms = load_yaml('/opt/netbox/initializers/platforms.yml')
+
+if platforms is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  platforms = yaml.load(stream)
+optional_assocs = {
+  'manufacturer': (Manufacturer, 'name'),
+}
 
-  optional_assocs = {
-    'manufacturer': (Manufacturer, 'name'),
-  }
+for params in platforms:
 
-  if platforms is not None:
-    for params in platforms:
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  platform, created = Platform.objects.get_or_create(**params)
 
-      platform, created = Platform.objects.get_or_create(**params)
-
-      if created:
-        print("πŸ’Ύ Created platform", platform.name)
+  if created:
+    print("πŸ’Ύ Created platform", platform.name)
diff --git a/startup_scripts/110_tenant_groups.py b/startup_scripts/110_tenant_groups.py
index eaf75dc..c106d67 100644
--- a/startup_scripts/110_tenant_groups.py
+++ b/startup_scripts/110_tenant_groups.py
@@ -1,19 +1,14 @@
 from tenancy.models import TenantGroup
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/tenant_groups.yml')
-if not file.is_file():
+tenant_groups = load_yaml('/opt/netbox/initializers/tenant_groups.yml')
+
+if tenant_groups is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  tenant_groups = yaml.load(stream)
+for params in tenant_groups:
+  tenant_group, created = TenantGroup.objects.get_or_create(**params)
 
-  if tenant_groups is not None:
-    for params in tenant_groups:
-      tenant_group, created = TenantGroup.objects.get_or_create(**params)
-
-      if created:
-        print("πŸ”³ Created Tenant Group", tenant_group.name)
+  if created:
+    print("πŸ”³ Created Tenant Group", tenant_group.name)
diff --git a/startup_scripts/120_tenants.py b/startup_scripts/120_tenants.py
index 8252dc7..121c83a 100644
--- a/startup_scripts/120_tenants.py
+++ b/startup_scripts/120_tenants.py
@@ -1,45 +1,39 @@
 from tenancy.models import Tenant, TenantGroup
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/tenants.yml')
-if not file.is_file():
+tenants = load_yaml('/opt/netbox/initializers/tenants.yml')
+
+if tenants is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  tenants = yaml.load(stream)
+optional_assocs = {
+  'group': (TenantGroup, 'name')
+}
 
-  optional_assocs = {
-    'group': (TenantGroup, 'name')
-  }
+for params in tenants:
+  custom_fields = params.pop('custom_fields', None)
 
-  if tenants is not None:
-    for params in tenants:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  tenant, created = Tenant.objects.get_or_create(**params)
 
-      tenant, created = Tenant.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=tenant,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=tenant,
-              value=cf_value
-            )
+        tenant.custom_field_values.add(custom_field_value)
 
-            tenant.custom_field_values.add(custom_field_value)
-
-        print("πŸ‘©β€πŸ’» Created Tenant", tenant.name)
+    print("πŸ‘©β€πŸ’» Created Tenant", tenant.name)
diff --git a/startup_scripts/130_devices.py b/startup_scripts/130_devices.py
index 4217549..1e0d894 100644
--- a/startup_scripts/130_devices.py
+++ b/startup_scripts/130_devices.py
@@ -3,63 +3,57 @@ from ipam.models import IPAddress
 from virtualization.models import Cluster
 from tenancy.models import Tenant
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/devices.yml')
-if not file.is_file():
+devices = load_yaml('/opt/netbox/initializers/devices.yml')
+
+if devices is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  devices = yaml.load(stream)
+required_assocs = {
+  'device_role': (DeviceRole, 'name'),
+  'device_type': (DeviceType, 'model'),
+  'site': (Site, 'name')
+}
 
-  required_assocs = {
-    'device_role': (DeviceRole, 'name'),
-    'device_type': (DeviceType, 'model'),
-    'site': (Site, 'name')
-  }
+optional_assocs = {
+  'tenant': (Tenant, 'name'),
+  'platform': (Platform, 'name'),
+  'rack': (Rack, 'name'),
+  'cluster': (Cluster, 'name'),
+  'primary_ip4': (IPAddress, 'address'),
+  'primary_ip6': (IPAddress, 'address')
+}
 
-  optional_assocs = {
-    'tenant': (Tenant, 'name'),
-    'platform': (Platform, 'name'),
-    'rack': (Rack, 'name'),
-    'cluster': (Cluster, 'name'),
-    'primary_ip4': (IPAddress, 'address'),
-    'primary_ip6': (IPAddress, 'address')
-  }
+for params in devices:
+  custom_fields = params.pop('custom_fields', None)
 
-  if devices is not None:
-    for params in devices:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  device, created = Device.objects.get_or_create(**params)
 
-      device, created = Device.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=device,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=device,
-              value=cf_value
-            )
+        device.custom_field_values.add(custom_field_value)
 
-            device.custom_field_values.add(custom_field_value)
-
-        print("πŸ–₯️  Created device", device.name)
+    print("πŸ–₯️  Created device", device.name)
diff --git a/startup_scripts/140_cluster_types.py b/startup_scripts/140_cluster_types.py
index 9bd4be1..d39933f 100644
--- a/startup_scripts/140_cluster_types.py
+++ b/startup_scripts/140_cluster_types.py
@@ -1,19 +1,14 @@
 from virtualization.models import ClusterType
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/cluster_types.yml')
-if not file.is_file():
+cluster_types = load_yaml('/opt/netbox/initializers/cluster_types.yml')
+
+if cluster_types is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  cluster_types = yaml.load(stream)
+for params in cluster_types:
+  cluster_type, created = ClusterType.objects.get_or_create(**params)
 
-  if cluster_types is not None:
-    for params in cluster_types:
-      cluster_type, created = ClusterType.objects.get_or_create(**params)
-
-      if created:
-        print("🧰 Created Cluster Type", cluster_type.name)
+  if created:
+    print("🧰 Created Cluster Type", cluster_type.name)
diff --git a/startup_scripts/150_rirs.py b/startup_scripts/150_rirs.py
index 9cee56f..8bcf51f 100644
--- a/startup_scripts/150_rirs.py
+++ b/startup_scripts/150_rirs.py
@@ -1,19 +1,14 @@
 from ipam.models import RIR
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/rirs.yml')
-if not file.is_file():
+rirs = load_yaml('/opt/netbox/initializers/rirs.yml')
+
+if rirs is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  rirs = yaml.load(stream)
+for params in rirs:
+  rir, created = RIR.objects.get_or_create(**params)
 
-  if rirs is not None:
-    for params in rirs:
-      rir, created = RIR.objects.get_or_create(**params)
-
-      if created:
-        print("πŸ—ΊοΈ Created RIR", rir.name)
+  if created:
+    print("πŸ—ΊοΈ Created RIR", rir.name)
diff --git a/startup_scripts/160_aggregates.py b/startup_scripts/160_aggregates.py
index f932709..0ffe9b0 100644
--- a/startup_scripts/160_aggregates.py
+++ b/startup_scripts/160_aggregates.py
@@ -1,46 +1,42 @@
 from ipam.models import Aggregate, RIR
-from ruamel.yaml import YAML
+
 from extras.models import CustomField, CustomFieldValue
 
 from netaddr import IPNetwork
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/aggregates.yml')
-if not file.is_file():
+aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml')
+
+if aggregates is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  aggregates = yaml.load(stream)
+required_assocs = {
+  'rir': (RIR, 'name')
+}
 
-  required_assocs = {
-    'rir': (RIR, 'name')
-  }
+for params in aggregates:
+  custom_fields = params.pop('custom_fields', None)
+  params['prefix'] = IPNetwork(params['prefix'])
 
-  if aggregates is not None:
-    for params in aggregates:
-      custom_fields = params.pop('custom_fields', None)
-      params['prefix'] = IPNetwork(params['prefix'])
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  aggregate, created = Aggregate.objects.get_or_create(**params)
 
-      aggregate, created = Aggregate.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=aggregate,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=aggregate,
-              value=cf_value
-            )
+        aggregate.custom_field_values.add(custom_field_value)
 
-            aggregate.custom_field_values.add(custom_field_value)
-
-        print("πŸ—žοΈ Created Aggregate", aggregate.prefix)
+    print("πŸ—žοΈ Created Aggregate", aggregate.prefix)
diff --git a/startup_scripts/170_clusters.py b/startup_scripts/170_clusters.py
index 597f73f..a7e2065 100644
--- a/startup_scripts/170_clusters.py
+++ b/startup_scripts/170_clusters.py
@@ -1,57 +1,51 @@
 from dcim.models import Site
 from virtualization.models import Cluster, ClusterType, ClusterGroup
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/clusters.yml')
-if not file.is_file():
+clusters = load_yaml('/opt/netbox/initializers/clusters.yml')
+
+if clusters is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  clusters = yaml.load(stream)
+required_assocs = {
+  'type': (ClusterType, 'name')
+}
 
-  required_assocs = {
-    'type': (ClusterType, 'name')
-  }
+optional_assocs = {
+  'site': (Site, 'name'),
+  'group': (ClusterGroup, 'name')
+}
 
-  optional_assocs = {
-    'site': (Site, 'name'),
-    'group': (ClusterGroup, 'name')
-  }
+for params in clusters:
+  custom_fields = params.pop('custom_fields', None)
 
-  if clusters is not None:
-    for params in clusters:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  cluster, created = Cluster.objects.get_or_create(**params)
 
-      cluster, created = Cluster.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=cluster,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=cluster,
-              value=cf_value
-            )
+        cluster.custom_field_values.add(custom_field_value)
 
-            cluster.custom_field_values.add(custom_field_value)
-
-        print("πŸ—„οΈ Created cluster", cluster.name)
+    print("πŸ—„οΈ Created cluster", cluster.name)
diff --git a/startup_scripts/180_vrfs.py b/startup_scripts/180_vrfs.py
index d611c04..496710d 100644
--- a/startup_scripts/180_vrfs.py
+++ b/startup_scripts/180_vrfs.py
@@ -1,46 +1,42 @@
 from ipam.models import VRF
 from tenancy.models import Tenant
-from ruamel.yaml import YAML
+
 from extras.models import CustomField, CustomFieldValue
 
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/vrfs.yml')
-if not file.is_file():
+vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml')
+
+if vrfs is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  vrfs = yaml.load(stream)
+optional_assocs = {
+  'tenant': (Tenant, 'name')
+}
 
-  optional_assocs = {
-    'tenant': (Tenant, 'name')
-  }
+for params in vrfs:
+  custom_fields = params.pop('custom_fields', None)
 
-  if vrfs is not None:
-    for params in vrfs:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  vrf, created = VRF.objects.get_or_create(**params)
 
-      vrf, created = VRF.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=vrf,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=vrf,
-              value=cf_value
-            )
+        vrf.custom_field_values.add(custom_field_value)
 
-            vrf.custom_field_values.add(custom_field_value)
-
-        print("πŸ“¦ Created VRF", vrf.name)
+    print("πŸ“¦ Created VRF", vrf.name)
diff --git a/startup_scripts/190_prefix_vlan_roles.py b/startup_scripts/190_prefix_vlan_roles.py
index 61fa606..72c8eee 100644
--- a/startup_scripts/190_prefix_vlan_roles.py
+++ b/startup_scripts/190_prefix_vlan_roles.py
@@ -1,19 +1,14 @@
 from ipam.models import Role
-from ruamel.yaml import YAML
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/prefix_vlan_roles.yml')
-if not file.is_file():
+roles = load_yaml('/opt/netbox/initializers/prefix_vlan_roles.yml')
+
+if roles is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  roles = yaml.load(stream)
+for params in roles:
+  role, created = Role.objects.get_or_create(**params)
 
-  if roles is not None:
-    for params in roles:
-      role, created = Role.objects.get_or_create(**params)
-
-      if created:
-        print("⛹️‍ Created Prefix/VLAN Role", role.name)
+  if created:
+    print("⛹️‍ Created Prefix/VLAN Role", role.name)
diff --git a/startup_scripts/200_vlan_groups.py b/startup_scripts/200_vlan_groups.py
index 54250fb..f8dc55d 100644
--- a/startup_scripts/200_vlan_groups.py
+++ b/startup_scripts/200_vlan_groups.py
@@ -1,46 +1,40 @@
 from dcim.models import Site
 from ipam.models import VLANGroup
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/vlan_groups.yml')
-if not file.is_file():
+vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml')
+
+if vlan_groups is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  vlan_groups = yaml.load(stream)
+optional_assocs = {
+  'site': (Site, 'name')
+}
 
-  optional_assocs = {
-    'site': (Site, 'name')
-  }
+for params in vlan_groups:
+  custom_fields = params.pop('custom_fields', None)
 
-  if vlan_groups is not None:
-    for params in vlan_groups:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  vlan_group, created = VLANGroup.objects.get_or_create(**params)
 
-      vlan_group, created = VLANGroup.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=vlan_group,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=vlan_group,
-              value=cf_value
-            )
+        vlan_group.custom_field_values.add(custom_field_value)
 
-            vlan_group.custom_field_values.add(custom_field_value)
-
-        print("🏘️ Created VLAN Group", vlan_group.name)
+    print("🏘️ Created VLAN Group", vlan_group.name)
diff --git a/startup_scripts/210_vlans.py b/startup_scripts/210_vlans.py
index ab6bd2b..ceab196 100644
--- a/startup_scripts/210_vlans.py
+++ b/startup_scripts/210_vlans.py
@@ -2,50 +2,44 @@ from dcim.models import Site
 from ipam.models import VLAN, VLANGroup, Role
 from tenancy.models import Tenant, TenantGroup
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/vlans.yml')
-if not file.is_file():
+vlans = load_yaml('/opt/netbox/initializers/vlans.yml')
+
+if vlans is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  vlans = yaml.load(stream)
+optional_assocs = {
+  'site': (Site, 'name'),
+  'tenant': (Tenant, 'name'),
+  'tenant_group': (TenantGroup, 'name'),
+  'group': (VLANGroup, 'name'),
+  'role': (Role, 'name')
+}
 
-  optional_assocs = {
-    'site': (Site, 'name'),
-    'tenant': (Tenant, 'name'),
-    'tenant_group': (TenantGroup, 'name'),
-    'group': (VLANGroup, 'name'),
-    'role': (Role, 'name')
-  }
+for params in vlans:
+  custom_fields = params.pop('custom_fields', None)
 
-  if vlans is not None:
-    for params in vlans:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  vlan, created = VLAN.objects.get_or_create(**params)
 
-      vlan, created = VLAN.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=vlan,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=vlan,
-              value=cf_value
-            )
+        vlan.custom_field_values.add(custom_field_value)
 
-            vlan.custom_field_values.add(custom_field_value)
-
-        print("🏠 Created VLAN", vlan.name)
+    print("🏠 Created VLAN", vlan.name)
diff --git a/startup_scripts/220_prefixes.py b/startup_scripts/220_prefixes.py
index d13578a..b047c8c 100644
--- a/startup_scripts/220_prefixes.py
+++ b/startup_scripts/220_prefixes.py
@@ -2,53 +2,45 @@ from dcim.models import Site
 from ipam.models import Prefix, VLAN, Role, VRF
 from tenancy.models import Tenant, TenantGroup
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
 from netaddr import IPNetwork
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/prefixes.yml')
-if not file.is_file():
+prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml')
+
+if prefixes is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  prefixes = yaml.load(stream)
+optional_assocs = {
+  'site': (Site, 'name'),
+  'tenant': (Tenant, 'name'),
+  'tenant_group': (TenantGroup, 'name'),
+  'vlan': (VLAN, 'name'),
+  'role': (Role, 'name'),
+  'vrf': (VRF, 'name')
+}
 
-  optional_assocs = {
-    'site': (Site, 'name'),
-    'tenant': (Tenant, 'name'),
-    'tenant_group': (TenantGroup, 'name'),
-    'vlan': (VLAN, 'name'),
-    'role': (Role, 'name'),
-    'vrf': (VRF, 'name')
-  }
+for params in prefixes:
+  custom_fields = params.pop('custom_fields', None)
+  params['prefix'] = IPNetwork(params['prefix'])
 
-  if prefixes is not None:
-    for params in prefixes:
-      custom_fields = params.pop('custom_fields', None)
-      params['prefix'] = IPNetwork(params['prefix'])
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+  prefix, created = Prefix.objects.get_or_create(**params)
 
-          params[assoc] = model.objects.get(**query)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=prefix,
+          value=cf_value
+        )
+        prefix.custom_field_values.add(custom_field_value)
 
-      prefix, created = Prefix.objects.get_or_create(**params)
-
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=prefix,
-              value=cf_value
-            )
-
-            prefix.custom_field_values.add(custom_field_value)
-
-        print("πŸ“Œ Created Prefix", prefix.prefix)
+    print("πŸ“Œ Created Prefix", prefix.prefix)
diff --git a/startup_scripts/230_virtual_machines.py b/startup_scripts/230_virtual_machines.py
index 449df8a..6776c4d 100644
--- a/startup_scripts/230_virtual_machines.py
+++ b/startup_scripts/230_virtual_machines.py
@@ -2,58 +2,52 @@ from dcim.models import Site, Platform, DeviceRole
 from virtualization.models import Cluster, VirtualMachine
 from tenancy.models import Tenant
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/virtual_machines.yml')
-if not file.is_file():
+virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml')
+
+if virtual_machines is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  virtual_machines = yaml.load(stream)
+required_assocs = {
+  'cluster': (Cluster, 'name')
+}
 
-  required_assocs = {
-    'cluster': (Cluster, 'name')
-  }
+optional_assocs = {
+  'tenant': (Tenant, 'name'),
+  'platform': (Platform, 'name'),
+  'role': (DeviceRole, 'name')
+}
 
-  optional_assocs = {
-    'tenant': (Tenant, 'name'),
-    'platform': (Platform, 'name'),
-    'role': (DeviceRole, 'name')
-  }
+for params in virtual_machines:
+  custom_fields = params.pop('custom_fields', None)
 
-  if virtual_machines is not None:
-    for params in virtual_machines:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      query = { field: params.pop(assoc) }
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-          params[assoc] = model.objects.get(**query)
+  virtual_machine, created = VirtualMachine.objects.get_or_create(**params)
 
-      virtual_machine, created = VirtualMachine.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=virtual_machine,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=virtual_machine,
-              value=cf_value
-            )
+        virtual_machine.custom_field_values.add(custom_field_value)
 
-            virtual_machine.custom_field_values.add(custom_field_value)
-
-        print("πŸ–₯️ Created virtual machine", virtual_machine.name)
+    print("πŸ–₯️ Created virtual machine", virtual_machine.name)
diff --git a/startup_scripts/240_virtualization_interfaces.py b/startup_scripts/240_virtualization_interfaces.py
index 8d2ca96..e5d75f7 100644
--- a/startup_scripts/240_virtualization_interfaces.py
+++ b/startup_scripts/240_virtualization_interfaces.py
@@ -1,45 +1,39 @@
 from dcim.models import Interface
 from virtualization.models import VirtualMachine
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/virtualization_interfaces.yml')
-if not file.is_file():
+interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml')
+
+if interfaces is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  interfaces = yaml.load(stream)
+required_assocs = {
+  'virtual_machine': (VirtualMachine, 'name')
+}
 
-  required_assocs = {
-    'virtual_machine': (VirtualMachine, 'name')
-  }
+for params in interfaces:
+  custom_fields = params.pop('custom_fields', None)
 
-  if interfaces is not None:
-    for params in interfaces:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  interface, created = Interface.objects.get_or_create(**params)
 
-      interface, created = Interface.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=interface,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=interface,
-              value=cf_value
-            )
+        interface.custom_field_values.add(custom_field_value)
 
-            interface.custom_field_values.add(custom_field_value)
-
-        print("🧷 Created interface", interface.name, interface.virtual_machine.name)
+    print("🧷 Created interface", interface.name, interface.virtual_machine.name)
diff --git a/startup_scripts/250_dcim_interfaces.py b/startup_scripts/250_dcim_interfaces.py
index ec30b5c..51f885b 100644
--- a/startup_scripts/250_dcim_interfaces.py
+++ b/startup_scripts/250_dcim_interfaces.py
@@ -1,44 +1,38 @@
 from dcim.models import Interface, Device
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
-
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/dcim_interfaces.yml')
-if not file.is_file():
+interfaces= load_yaml('/opt/netbox/initializers/dcim_interfaces.yml')
+
+if interfaces is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  interfaces = yaml.load(stream)
+required_assocs = {
+  'device': (Device, 'name')
+}
 
-  required_assocs = {
-    'device': (Device, 'name')
-  }
+for params in interfaces:
+  custom_fields = params.pop('custom_fields', None)
 
-  if interfaces is not None:
-    for params in interfaces:
-      custom_fields = params.pop('custom_fields', None)
+  for assoc, details in required_assocs.items():
+    model, field = details
+    query = { field: params.pop(assoc) }
 
-      for assoc, details in required_assocs.items():
-        model, field = details
-        query = { field: params.pop(assoc) }
+    params[assoc] = model.objects.get(**query)
 
-        params[assoc] = model.objects.get(**query)
+  interface, created = Interface.objects.get_or_create(**params)
 
-      interface, created = Interface.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=interface,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=interface,
-              value=cf_value
-            )
+        interface.custom_field_values.add(custom_field_value)
 
-            interface.custom_field_values.add(custom_field_value)
-
-        print("🧷 Created interface", interface.name, interface.device.name)
+    print("🧷 Created interface", interface.name, interface.device.name)
diff --git a/startup_scripts/260_ip_addresses.py b/startup_scripts/260_ip_addresses.py
index d109a36..96ec4b0 100644
--- a/startup_scripts/260_ip_addresses.py
+++ b/startup_scripts/260_ip_addresses.py
@@ -3,63 +3,58 @@ from dcim.models import Device, Interface
 from virtualization.models import VirtualMachine
 from tenancy.models import Tenant
 from extras.models import CustomField, CustomFieldValue
-from ruamel.yaml import YAML
 
 from netaddr import IPNetwork
-from pathlib import Path
+from startup_script_utils import load_yaml
 import sys
 
-file = Path('/opt/netbox/initializers/ip_addresses.yml')
-if not file.is_file():
+ip_addresses = load_yaml('/opt/netbox/initializers/ip_addresses.yml')
+
+if ip_addresses is None:
   sys.exit()
 
-with file.open('r') as stream:
-  yaml = YAML(typ='safe')
-  ip_addresses = yaml.load(stream)
+optional_assocs = {
+  'tenant': (Tenant, 'name'),
+  'vrf': (VRF, 'name'),
+  'interface': (Interface, 'name')
+}
 
-  optional_assocs = {
-    'tenant': (Tenant, 'name'),
-    'vrf': (VRF, 'name'),
-    'interface': (Interface, 'name')
-  }
+for params in ip_addresses:
+  vm = params.pop('virtual_machine', None)
+  device = params.pop('device', None)
+  custom_fields = params.pop('custom_fields', None)
+  params['address'] = IPNetwork(params['address'])
 
-  if ip_addresses is not None:
-    for params in ip_addresses:
-      vm = params.pop('virtual_machine', None)
-      device = params.pop('device', None)
-      custom_fields = params.pop('custom_fields', None)
-      params['address'] = IPNetwork(params['address'])
+  if vm and device:
+    print("IP Address can only specify one of the following: virtual_machine or device.")
+    sys.exit()
 
-      if vm and device:
-        print("IP Address can only specify one of the following: virtual_machine or device.")
-        sys.exit()
+  for assoc, details in optional_assocs.items():
+    if assoc in params:
+      model, field = details
+      if assoc == 'interface':
+          if vm:
+              vm_id = VirtualMachine.objects.get(name=vm).id
+              query = { field: params.pop(assoc), "virtual_machine_id": vm_id }
+          elif device:
+              dev_id = Device.objects.get(name=device).id
+              query = { field: params.pop(assoc), "device_id": dev_id }
+      else:
+          query = { field: params.pop(assoc) }
+      params[assoc] = model.objects.get(**query)
 
-      for assoc, details in optional_assocs.items():
-        if assoc in params:
-          model, field = details
-          if assoc == 'interface':
-              if vm:
-                  vm_id = VirtualMachine.objects.get(name=vm).id
-                  query = { field: params.pop(assoc), "virtual_machine_id": vm_id }
-              elif device:
-                  dev_id = Device.objects.get(name=device).id
-                  query = { field: params.pop(assoc), "device_id": dev_id }
-          else:
-              query = { field: params.pop(assoc) }
-          params[assoc] = model.objects.get(**query)
+  ip_address, created = IPAddress.objects.get_or_create(**params)
 
-      ip_address, created = IPAddress.objects.get_or_create(**params)
+  if created:
+    if custom_fields is not None:
+      for cf_name, cf_value in custom_fields.items():
+        custom_field = CustomField.objects.get(name=cf_name)
+        custom_field_value = CustomFieldValue.objects.create(
+          field=custom_field,
+          obj=ip_address,
+          value=cf_value
+        )
 
-      if created:
-        if custom_fields is not None:
-          for cf_name, cf_value in custom_fields.items():
-            custom_field = CustomField.objects.get(name=cf_name)
-            custom_field_value = CustomFieldValue.objects.create(
-              field=custom_field,
-              obj=ip_address,
-              value=cf_value
-            )
+        ip_address.custom_field_values.add(custom_field_value)
 
-            ip_address.custom_field_values.add(custom_field_value)
-
-        print("🧬 Created IP Address", ip_address.address)
+    print("🧬 Created IP Address", ip_address.address)
diff --git a/startup_scripts/startup_script_utils/__init__.py b/startup_scripts/startup_script_utils/__init__.py
new file mode 100644
index 0000000..7a37f57
--- /dev/null
+++ b/startup_scripts/startup_script_utils/__init__.py
@@ -0,0 +1 @@
+from .load_yaml import load_yaml
diff --git a/startup_scripts/startup_script_utils/load_yaml.py b/startup_scripts/startup_script_utils/load_yaml.py
new file mode 100644
index 0000000..4c16816
--- /dev/null
+++ b/startup_scripts/startup_script_utils/load_yaml.py
@@ -0,0 +1,10 @@
+from ruamel.yaml import YAML
+from pathlib import Path
+
+def load_yaml(yaml_file: str):
+  yf = Path(yaml_file)
+  if not yf.is_file():
+    return None
+  with yf.open("r") as stream:
+    yaml = YAML(typ="safe")
+    return yaml.load(stream)