diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py
index efadf0bc0a4830011a8403d35f36ca9f49645a4d..0c7176deb5dd41b6a1c6d7cf5dcea30a2992a4ba 100644
--- a/gso/services/infoblox.py
+++ b/gso/services/infoblox.py
@@ -12,6 +12,7 @@ from infoblox_client.exceptions import (
 from gso.settings import IPAMParams, load_oss_params
 
 logger = getLogger(__name__)
+NULL_MAC = "00:00:00:00:00:00"
 
 
 class AllocationError(Exception):
@@ -40,11 +41,7 @@ def _setup_connection() -> tuple[connector.Connector, IPAMParams]:
 
 
 def _allocate_network(
-    conn: connector.Connector,
-    dns_view: str,
-    netmask: int,
-    containers: list[str],
-    comment: str | None = "",
+    conn: connector.Connector, dns_view: str, netmask: int, containers: list[str], comment: str | None = ""
 ) -> ipaddress.IPv4Network | ipaddress.IPv6Network:
     """Allocate a new network in Infoblox.
 
@@ -160,10 +157,7 @@ def delete_network(ip_network: ipaddress.IPv4Network | ipaddress.IPv6Network) ->
 
 
 def allocate_host(
-    hostname: str,
-    service_type: str,
-    cname_aliases: list[str],
-    comment: str,
+    hostname: str, service_type: str, cname_aliases: list[str], comment: str
 ) -> tuple[ipaddress.IPv4Address, ipaddress.IPv6Address]:
     """Allocate a new host record in Infoblox.
 
@@ -194,7 +188,7 @@ def allocate_host(
     created_v6 = None
     for ipv6_range in allocation_networks_v6:
         v6_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, str(ipv6_range))
-        ipv6_object = objects.IP.create(ip=v6_alloc, mac="00:00:00:00:00:00", configure_for_dhcp=False)
+        ipv6_object = objects.IP.create(ip=v6_alloc, mac=NULL_MAC, configure_for_dhcp=False)
         try:
             new_host = objects.HostRecord.create(
                 conn,
@@ -216,7 +210,7 @@ def allocate_host(
     created_v4 = None
     for ipv4_range in allocation_networks_v4:
         v4_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, str(ipv4_range))
-        ipv4_object = objects.IP.create(ip=v4_alloc, mac="00:00:00:00:00:00", configure_for_dhcp=False)
+        ipv4_object = objects.IP.create(ip=v4_alloc, mac=NULL_MAC, configure_for_dhcp=False)
         new_host = objects.HostRecord.search(conn, name=hostname)
         new_host.ipv4addrs = [ipv4_object]
         try:
@@ -234,9 +228,39 @@ def allocate_host(
     return created_v4, created_v6
 
 
-def find_host_by_ip(
-    ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address,
-) -> objects.HostRecord | None:
+def create_host_by_ip(
+    hostname: str,
+    ipv4_address: ipaddress.IPv4Address,
+    ipv6_address: ipaddress.IPv6Address,
+    service_type: str,
+    comment: str,
+) -> None:
+    """Create a new host record with a given IPv4 and IPv6 address.
+
+    :param str hostname: The :term:`FQDN` of the new host.
+    :param IPv4Address ipv4_address: The IPv4 address of the new host.
+    :param IPv6Address ipv6_address: The IPv6 address of the new host.
+    :param str service_type: The relevant service type, used to deduce the correct ``dns_view`` in Infoblox.
+    :param str comment: The comment stored in this Infoblox record, most likely the relevant ``subscription_id`` in
+                        :term:`GSO`.
+    """
+    if not hostname_available(hostname):
+        msg = f"Cannot allocate new host, FQDN {hostname} already taken."
+        raise AllocationError(msg)
+
+    conn, oss = _setup_connection()
+    ipv6_object = objects.IP.create(ip=ipv6_address, mac=NULL_MAC, configure_for_dhcp=False)
+    ipv4_object = objects.IP.create(ip=ipv4_address, mac=NULL_MAC, configure_for_dhcp=False)
+    dns_view = getattr(oss, service_type).dns_view
+
+    # This needs to be done in two steps, otherwise only one of the IP addresses is stored.
+    objects.HostRecord.create(conn, ip=ipv6_object, name=hostname, comment=comment, dns_view=dns_view)
+    new_host = find_host_by_fqdn(hostname)
+    new_host.ipv4addrs = [ipv4_object]
+    new_host.update()
+
+
+def find_host_by_ip(ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> objects.HostRecord | None:
     """Find a host record in Infoblox by its associated IP address.
 
     :param ip_addr: The IP address of a host that is searched for.
@@ -249,14 +273,14 @@ def find_host_by_ip(
             ipv4addr=ip_addr,
             return_fields=["ipv4addrs", "name", "view", "aliases", "comment"],
         )
-    return objects.HostRecord.search(
+    return objects.HostRecordV6.search(
         conn,
         ipv6addr=ip_addr,
         return_fields=["ipv6addrs", "name", "view", "aliases", "comment"],
     )
 
 
-def find_host_by_fqdn(fqdn: str) -> objects.HostRecord | None:
+def find_host_by_fqdn(fqdn: str) -> objects.HostRecord:
     """Find a host record by its associated :term:`FQDN`.
 
     :param fqdn: The :term:`FQDN` of a host that is searched for.
@@ -270,6 +294,18 @@ def find_host_by_fqdn(fqdn: str) -> objects.HostRecord | None:
     )
 
 
+def find_v6_host_by_fqdn(fqdn: str) -> objects.HostRecordV6:
+    """Find a host record by its associated :term:`FQDN`.
+
+    This specific method will return the IPv6 variant of a record, if it exists.
+    :param str fqdn: The :term:`FQDN` of a host that is searched for.
+    """
+    conn, _ = _setup_connection()
+    return objects.HostRecordV6.search(
+        conn, name=fqdn, return_fields=["ipv6addrs", "name", "view", "aliases", "comment"]
+    )
+
+
 def delete_host_by_ip(ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> None:
     """Delete a host from Infoblox.
 
diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index 14190a6000ea14d429c27cd0f96e75ddea7f7154..4a754ae0ae540e8d52d93d11231d9278321b10f7 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -16,6 +16,7 @@ from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Choice, Label, UniqueConstrainedList
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, UUIDstr
+from orchestrator.utils.errors import ProcessFailureError
 from orchestrator.utils.json import json_dumps
 from orchestrator.workflow import StepList, conditional, done, init, inputstep
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
@@ -23,11 +24,13 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from pydantic import validator
 from pydantic_forms.core import ReadOnlyField
 from pynetbox.models.dcim import Interfaces
+from services.infoblox import DeletionError
 
 from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
 from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
+from gso.services import infoblox
 from gso.services.netbox_client import NetboxClient
 from gso.services.provisioning_proxy import execute_playbook, pp_interaction
 from gso.services.subscriptions import get_active_router_subscriptions
@@ -167,6 +170,50 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     )
 
 
+@step("Netbox: Reserve new interfaces")
+def netbox_reserve_interfaces(
+    subscription: Iptrunk, new_node: UUIDstr, new_lag_interface: str, new_lag_member_interfaces: list[dict]
+) -> State:
+    """Reserve new interfaces in Netbox, only when the new side's router is a NOKIA router."""
+    new_side = Router.from_subscription(new_node).router
+    nbclient = NetboxClient()
+    # Create :term:`LAG` interfaces
+    lag_interface: Interfaces = nbclient.create_interface(
+        iface_name=new_lag_interface,
+        interface_type="lag",
+        device_name=new_side.router_fqdn,
+        description=str(subscription.subscription_id),
+        enabled=True,
+    )
+    # Attach physical interfaces to :term:`LAG`
+    # Reserve interfaces
+    for interface in new_lag_member_interfaces:
+        nbclient.attach_interface_to_lag(
+            device_name=new_side.router_fqdn,
+            lag_name=lag_interface.name,
+            iface_name=interface["interface_name"],
+            description=str(subscription.subscription_id),
+        )
+        nbclient.reserve_interface(
+            device_name=new_side.router_fqdn,
+            iface_name=interface["interface_name"],
+        )
+    return {"subscription": subscription}
+
+
+@step("Calculate old side data")
+def calculate_old_side_data(subscription: Iptrunk, replace_index: int) -> State:
+    """Store subscription information of the old side in the state of the workflow for later use."""
+    old_subscription = copy.deepcopy(subscription)
+    old_side_data = {
+        "iptrunk_side_node": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_node,
+        "iptrunk_side_ae_iface": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_iface,
+        "iptrunk_side_ae_members": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_members,
+    }
+
+    return {"old_side_data": old_side_data}
+
+
 @step("[DRY RUN] Disable configuration on old router")
 def disable_old_config_dry(
     subscription: Iptrunk,
@@ -488,12 +535,28 @@ def delete_old_config_real(
     return {"subscription": subscription}
 
 
-@step("Update IPAM")
-def update_ipam(subscription: Iptrunk) -> State:
+@step("Update IP records in IPAM")
+def update_ipam(subscription: Iptrunk, old_side_data: dict, new_node: Router, new_lag_interface: str) -> State:
     """Update :term:`IPAM` resources.
 
-    TODO: implement
+    Move the DNS record pointing to the old side of the trunk, to the new side.
     """
+    old_fqdn = f"{old_side_data['iptrunk_side_ae_iface']}.{old_side_data['iptrunk_side_node']['router_fqdn']}"
+    trunk_v4 = infoblox.find_host_by_fqdn(old_fqdn)
+    trunk_v6 = infoblox.find_v6_host_by_fqdn(old_fqdn)
+
+    #  Out with the old
+    try:
+        infoblox.delete_host_by_fqdn(old_fqdn)
+    except DeletionError as e:
+        msg = "Failed to delete record from Infoblox."
+        raise ProcessFailureError(msg) from e
+
+    #  And in with the new
+    new_fqdn = f"{new_lag_interface}.{new_node.router.router_fqdn}"
+    comment = str(subscription.subscription_id)
+    infoblox.create_host_by_ip(new_fqdn, trunk_v4.ipv4addr, trunk_v6.ipv6addr, service_type="TRUNK", comment=comment)
+
     return {"subscription": subscription}
 
 
@@ -507,12 +570,6 @@ def update_subscription_model(
 ) -> State:
     """Update the subscription model in the database."""
     # Deep copy of subscription data
-    old_subscription = copy.deepcopy(subscription)
-    old_side_data = {
-        "iptrunk_side_node": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_node,
-        "iptrunk_side_ae_iface": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_iface,
-        "iptrunk_side_ae_members": old_subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_members,
-    }
     subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_node = Router.from_subscription(new_node).router
     subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_iface = new_lag_interface
     subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_ae_members.clear()
@@ -522,40 +579,6 @@ def update_subscription_model(
             IptrunkInterfaceBlock.new(subscription_id=uuid4(), **member),
         )
 
-    return {"subscription": subscription, "old_side_data": old_side_data}
-
-
-@step("Netbox: Reserve new interfaces")
-def netbox_reserve_interfaces(
-    subscription: Iptrunk,
-    new_node: UUIDstr,
-    new_lag_interface: str,
-    new_lag_member_interfaces: list[dict],
-) -> State:
-    """Reserve new interfaces in Netbox, only when the new side's router is a NOKIA router."""
-    new_side = Router.from_subscription(new_node).router
-    nbclient = NetboxClient()
-    # Create :term:`LAG` interfaces
-    lag_interface: Interfaces = nbclient.create_interface(
-        iface_name=new_lag_interface,
-        interface_type="lag",
-        device_name=new_side.router_fqdn,
-        description=str(subscription.subscription_id),
-        enabled=True,
-    )
-    # Attach physical interfaces to :term:`LAG`
-    # Reserve interfaces
-    for interface in new_lag_member_interfaces:
-        nbclient.attach_interface_to_lag(
-            device_name=new_side.router_fqdn,
-            lag_name=lag_interface.name,
-            iface_name=interface["interface_name"],
-            description=str(subscription.subscription_id),
-        )
-        nbclient.reserve_interface(
-            device_name=new_side.router_fqdn,
-            iface_name=interface["interface_name"],
-        )
     return {"subscription": subscription}
 
 
@@ -628,6 +651,7 @@ def migrate_iptrunk() -> StepList:
         >> store_process_subscription(Target.MODIFY)
         >> unsync
         >> new_side_is_nokia(netbox_reserve_interfaces)
+        >> calculate_old_side_data
         >> pp_interaction(set_isis_to_90000)
         >> pp_interaction(disable_old_config_dry)
         >> pp_interaction(disable_old_config_real)