diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5e54ad156d4279d0bba424c9ee36aeff2548dc19..0ed900004f54ad77ce3c2cf9a1a327e498f54e83 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,7 +47,7 @@ run-tox-pipeline:
 
 sonarqube:
   stage: sonarqube
-  image: sonarsource/sonar-scanner-cli
+  image: sonarsource/sonar-scanner-cli:10.0
   script:
     - sonar-scanner -Dsonar.login=$SONAR_TOKEN -Dproject.settings=./sonar.properties
   tags:
diff --git a/Changelog.md b/Changelog.md
index cead4f9864c1a03b5ad75134e9d577931ebd4bc6..2df8db9ae19a869f38b9012ab4ad6d9b343f7bd6 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,4 +1,19 @@
 # Changelog
+
+## [2.18] - 2024-10-01
+-  Use solo pool for Celery workers
+
+## [2.17] - 2024-09-30
+- NOTHING IS HERE (JENKINS ISSUE)
+
+## [2.16] - 2024-09-30
+- NOTHING IS HERE (JENKINS ISSUE)
+
+## [2.15] - 2024-09-30
+- Show current license usage when updating Kentik license of a router
+- Fix the bug of clearing all the AE members and creating new objects instead of updating it.
+- make orchestrator-core==2.7.5
+
 ## [2.14] - 2024-09-19
 - Fixes to Infoblox client.
 
diff --git a/gso/utils/workflow_steps.py b/gso/utils/workflow_steps.py
index a67558c4fc3f73d2096c51e394b8f111fb1fa282..a595bbc00b0176fcdbec51d29749d54d412f2f1d 100644
--- a/gso/utils/workflow_steps.py
+++ b/gso/utils/workflow_steps.py
@@ -1,6 +1,7 @@
 """Workflow steps that are shared across multiple workflows."""
 
 import json
+from operator import inv
 from typing import Any
 
 from orchestrator import inputstep, step
@@ -14,7 +15,7 @@ from pydantic_forms.validators import Label
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.iptrunk import Iptrunk
-from gso.services import lso_client
+from gso.services.lso_client import LSOState
 from gso.settings import load_oss_params
 from gso.utils.helpers import generate_inventory_for_active_routers
 from gso.utils.shared_enums import Vendor
@@ -23,13 +24,10 @@ from gso.utils.shared_enums import Vendor
 def _deploy_base_config(
     subscription: dict[str, Any],
     tt_number: str,
-    callback_route: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
-    inventory = subscription["router"]["router_fqdn"]
-
+) -> LSOState:
     extra_vars = {
         "wfo_router_json": subscription,
         "dry_run": dry_run,
@@ -37,27 +35,23 @@ def _deploy_base_config(
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy base config",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="base_config.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "base_config.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 def _update_sdp_mesh(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
+) -> LSOState:
     inventory = generate_inventory_for_active_routers(
         router_role=RouterRole.PE, router_vendor=Vendor.NOKIA, exclude_routers=[subscription["router"]["router_fqdn"]]
     )
-    if len(inventory["all"]["hosts"].keys()) == 0:
-        return  # Skip this Ansible interaction if the inventory is empty.
 
     extra_vars = {
         "dry_run": dry_run,
@@ -73,23 +67,20 @@ def _update_sdp_mesh(
         },
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_pe_sdp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_pe_sdp_mesh.yaml",
+        "inventory": inventory,
+        "extra_vars": extra_vars,
+    }
 
 
 def _update_sdp_single_pe(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
-    inventory = subscription["router"]["router_fqdn"]
+) -> LSOState:
     extra_vars = {
         "dry_run": dry_run,
         "subscription": subscription,
@@ -103,23 +94,20 @@ def _update_sdp_single_pe(
         )["all"]["hosts"],
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_pe_sdp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_pe_sdp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 def _add_pe_mesh_to_pe(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
-    inventory = subscription["router"]["router_fqdn"]
+) -> LSOState:
     extra_vars = {
         "dry_run": dry_run,
         "subscription": subscription,
@@ -131,22 +119,20 @@ def _add_pe_mesh_to_pe(
         )["all"]["hosts"],
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 def _add_pe_to_pe_mesh(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
+) -> LSOState:
     inventory = generate_inventory_for_active_routers(
         router_role=RouterRole.PE, exclude_routers=[subscription["router"]["router_fqdn"]]
     )
@@ -158,23 +144,20 @@ def _add_pe_to_pe_mesh(
         "verb": "add_pe_to_pe_mesh",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": inventory,
+        "extra_vars": extra_vars,
+    }
 
 
 def _add_all_p_to_pe(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
-    inventory = subscription["router"]["router_fqdn"]
+) -> LSOState:
     extra_vars = {
         "dry_run": dry_run,
         "subscription": subscription,
@@ -185,22 +168,20 @@ def _add_all_p_to_pe(
         )["all"]["hosts"],
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 def _add_pe_to_all_p(
     subscription: dict[str, Any],
-    callback_route: str,
     tt_number: str,
     process_id: UUIDstr,
     *,
     dry_run: bool,
-) -> None:
+) -> LSOState:
     inventory = generate_inventory_for_active_routers(
         router_role=RouterRole.P, exclude_routers=[subscription["router"]["router_fqdn"]]
     )
@@ -212,258 +193,99 @@ def _add_pe_to_all_p(
         "verb": "add_pe_to_all_p",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=inventory,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": inventory,
+        "extra_vars": extra_vars,
+    }
+
+
+@step("[DRY RUN] Deploy base config")
+def deploy_base_config_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
+    """Perform a dry run of provisioning base config on a router."""
+    return _deploy_base_config(subscription, tt_number, process_id, dry_run=True)
+
+
+@step("[FOR REAL] Deploy base config")
+def deploy_base_config_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
+    """Deploy base config on a router using the provisioning proxy."""
+    return _deploy_base_config(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Add the PE to all P routers")
-def add_pe_to_all_p_dry(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_pe_to_all_p_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of adding the PE router to all P routers."""
-    _add_pe_to_all_p(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _add_pe_to_all_p(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Add the PE to all P routers")
-def add_pe_to_all_p_real(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_pe_to_all_p_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a real run of adding the PE router to all P routers."""
-    _add_pe_to_all_p(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
+    return _add_pe_to_all_p(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Add all P routers to the PE")
-def add_all_p_to_pe_dry(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_all_p_to_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of adding all P routers to the PE router."""
-    _add_all_p_to_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _add_all_p_to_pe(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Add all P routers to the PE")
-def add_all_p_to_pe_real(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_all_p_to_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a real run of adding all P routers to the PE router."""
-    _add_all_p_to_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
+    return _add_all_p_to_pe(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Add the PE to PE mesh")
-def add_pe_to_pe_mesh_dry(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_pe_to_pe_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of adding the PE router to all PE routers in iGEANT/iGEANT6."""
-    _add_pe_to_pe_mesh(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _add_pe_to_pe_mesh(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Add the PE to PE mesh")
-def add_pe_to_pe_mesh_real(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
-    """Perform a dry run of adding the PE router to all PE routers in iGEANT/iGEANT6."""
-    _add_pe_to_pe_mesh(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
+def add_pe_to_pe_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
+    """Perform a real run of adding the PE router to all PE routers in iGEANT/iGEANT6."""
+    return _add_pe_to_pe_mesh(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Add PE mesh to the PE")
-def add_pe_mesh_to_pe_dry(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_pe_mesh_to_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of adding list of PE routers into iGEANT/iGEANT6 of the router."""
-    _add_pe_mesh_to_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _add_pe_mesh_to_pe(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Add PE mesh to the PE")
-def add_pe_mesh_to_pe_real(
-    subscription: dict[str, Any],
-    callback_route: str,
-    tt_number: str,
-    process_id: UUIDstr,
-) -> State:
+def add_pe_mesh_to_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a real run of adding list of PE routers into iGEANT/iGEANT6 of the router."""
-    _add_pe_mesh_to_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
-
-
-@step("[DRY RUN] Deploy base config")
-def deploy_base_config_dry(
-    subscription: dict[str, Any],
-    tt_number: str,
-    callback_route: str,
-    process_id: UUIDstr,
-) -> State:
-    """Perform a dry run of provisioning base config on a router."""
-    _deploy_base_config(subscription, tt_number, callback_route, process_id, dry_run=True)
-
-    return {"subscription": subscription}
-
-
-@step("[FOR REAL] Deploy base config")
-def deploy_base_config_real(
-    subscription: dict[str, Any],
-    tt_number: str,
-    callback_route: str,
-    process_id: UUIDstr,
-) -> State:
-    """Deploy base config on a router using the provisioning proxy."""
-    _deploy_base_config(subscription, tt_number, callback_route, process_id, dry_run=False)
-
-    return {"subscription": subscription}
+    return _add_pe_mesh_to_pe(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Include the PE into SDP mesh on other Nokia PEs")
-def update_sdp_mesh_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> State:
+def update_sdp_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of including new PE router in SDP mesh on other NOKIA PE routers."""
-    _update_sdp_mesh(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _update_sdp_mesh(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Include the PE into SDP mesh on other Nokia PEs")
-def update_sdp_mesh_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> State:
-    """Include new PE router in SDP mesh on other NOKIA PE routers."""
-    _update_sdp_mesh(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
+def update_sdp_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
+    """Perform a real run of including new PE router in SDP mesh on other NOKIA PE routers."""
+    return _update_sdp_mesh(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[DRY RUN] Configure SDP on the PE to all other Nokia PEs")
-def update_sdp_single_pe_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> State:
+def update_sdp_single_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Perform a dry run of configuring SDP on a new PE router to all other NOKIA PE routers."""
-    _update_sdp_single_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=True,
-    )
-
-    return {"subscription": subscription}
+    return _update_sdp_single_pe(subscription, tt_number, process_id, dry_run=True)
 
 
 @step("[FOR REAL] Configure SDP on the PE to all other Nokia PEs")
-def update_sdp_single_pe_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> State:
+def update_sdp_single_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State:
     """Configure SDP on a new PE router to all other NOKIA PE routers."""
-    _update_sdp_single_pe(
-        subscription=subscription,
-        tt_number=tt_number,
-        callback_route=callback_route,
-        process_id=process_id,
-        dry_run=False,
-    )
-
-    return {"subscription": subscription}
+    return _update_sdp_single_pe(subscription, tt_number, process_id, dry_run=False)
 
 
 @step("[FOR REAL] Set ISIS metric to very high value")
-def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
+def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Workflow step for setting the :term:`ISIS` metric to an arbitrarily high value to drain a link."""
     old_isis_metric = subscription.iptrunk.iptrunk_isis_metric
     params = load_oss_params()
@@ -477,33 +299,34 @@ def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, callback_route:
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
     return {
         "subscription": subscription,
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
         "old_isis_metric": old_isis_metric,
     }
 
 
 @step("Run show commands after base config install")
-def run_checks_after_base_config(subscription: dict[str, Any], callback_route: str) -> None:
+def run_checks_after_base_config(subscription: dict[str, Any]) -> LSOState:
     """Workflow step for running show commands after installing base config."""
-    lso_client.execute_playbook(
-        playbook_name="base_config_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars={"wfo_router_json": subscription},
-    )
+    return {
+        "playbook_name": "base_config_checks.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]}}},
+        "extra_vars": {"wfo_router_json": subscription},
+    }
 
 
-@step("Check PE iBGP sessions")
-def check_pe_ibgp(subscription: dict[str, Any], callback_route: str) -> None:
+@step("Check iBGP session")
+def check_pe_ibgp(subscription: dict[str, Any]) -> LSOState:
     """Check the iBGP session."""
     extra_vars = {
         "dry_run": False,
@@ -511,16 +334,15 @@ def check_pe_ibgp(subscription: dict[str, Any], callback_route: str) -> None:
         "verb": "check_pe_ibgp",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="check_ibgp.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "check_ibgp.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
-@step("Check L3 VPRN services")
-def check_l3_services(subscription: dict[str, Any], callback_route: str) -> None:
+@step("Check L3 services")
+def check_l3_services(subscription: dict[str, Any]) -> LSOState:
     """Check L3 services."""
     extra_vars = {
         "dry_run": False,
@@ -528,12 +350,11 @@ def check_l3_services(subscription: dict[str, Any], callback_route: str) -> None
         "verb": "check_base_ris",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="check_l3_services.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "check_l3_services.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @inputstep("Prompt for new SharePoint checklist", assignee=Assignee.SYSTEM)
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 56907a6c22e52c5f7d82642563e78d0a62b09853..ae3c6a74a54054b6a4fe07cd2cef9e9ee75ff3fa 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -28,7 +28,7 @@ from gso.products.product_blocks.iptrunk import (
 from gso.products.product_types.iptrunk import Iptrunk, IptrunkInactive, IptrunkProvisioning
 from gso.products.product_types.router import Router
 from gso.services import infoblox, subscriptions
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.services.partners import get_partner_by_name
 from gso.services.sharepoint import SharePointClient
@@ -325,12 +325,7 @@ def initialize_subscription(
 
 
 @step("[DRY RUN] Provision IP trunk interface")
-def provision_ip_trunk_iface_dry(
-    subscription: IptrunkInactive,
-    callback_route: str,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_iface_dry(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Perform a dry run of deploying configuration on both sides of the trunk."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -341,24 +336,22 @@ def provision_ip_trunk_iface_dry(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Provision IP trunk interface")
-def provision_ip_trunk_iface_real(
-    subscription: IptrunkInactive,
-    callback_route: str,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_iface_real(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Deploy IP trunk configuration on both sides."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -369,42 +362,34 @@ def provision_ip_trunk_iface_real(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check IP connectivity of the trunk")
-def check_ip_trunk_connectivity(
-    subscription: IptrunkInactive,
-    callback_route: str,
-) -> State:
+def check_ip_trunk_connectivity(subscription: IptrunkInactive) -> LSOState:
     """Check successful connectivity across the new trunk."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn,  # type: ignore[arg-type]
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {"all": {"hosts": {subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Provision IP trunk ISIS interface")
-def provision_ip_trunk_isis_iface_dry(
-    subscription: IptrunkInactive,
-    callback_route: str,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_isis_iface_dry(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Perform a dry run of deploying :term:`ISIS` configuration."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -415,24 +400,22 @@ def provision_ip_trunk_isis_iface_dry(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Provision IP trunk ISIS interface")
-def provision_ip_trunk_isis_iface_real(
-    subscription: IptrunkInactive,
-    callback_route: str,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_isis_iface_real(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Deploy :term:`ISIS` configuration on both sides."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -443,33 +426,30 @@ def provision_ip_trunk_isis_iface_real(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check ISIS adjacency")
-def check_ip_trunk_isis(
-    subscription: IptrunkInactive,
-    callback_route: str,
-) -> State:
+def check_ip_trunk_isis(subscription: IptrunkInactive) -> LSOState:
     """Run an Ansible playbook to confirm :term:`ISIS` adjacency."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "isis"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn,  # type: ignore[arg-type]
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {"all": {"hosts": {subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Register DNS records for both sides of the trunk")
diff --git a/gso/workflows/iptrunk/deploy_twamp.py b/gso/workflows/iptrunk/deploy_twamp.py
index 11dce53ba260fd8c8dae08611ae66e4069619d49..b43de54c60885b130c9ad57d8bc5a3adf435c084 100644
--- a/gso/workflows/iptrunk/deploy_twamp.py
+++ b/gso/workflows/iptrunk/deploy_twamp.py
@@ -5,14 +5,14 @@ import json
 from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Label
 from orchestrator.targets import Target
-from orchestrator.types import FormGenerator, State, UUIDstr
+from orchestrator.types import FormGenerator, UUIDstr
 from orchestrator.utils.json import json_dumps
 from orchestrator.workflow import StepList, begin, done, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_types.iptrunk import Iptrunk
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.utils.types.tt_number import TTNumber
 
 
@@ -33,7 +33,7 @@ def _initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
 
 @step("[DRY RUN] Deploy TWAMP on both sides")
-def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
+def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Perform a dry run of deploying the :term:`TWAMP` session."""
     extra_vars = {
         "subscription": json.loads(json_dumps(subscription)),
@@ -43,18 +43,22 @@ def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route:
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP",
     }
 
-    inventory = (
-        f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}"
-        f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}"
-    )
-
-    execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars)
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "deploy_twamp.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deploy TWAMP on both sides")
-def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
+def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Deploy the :term:`TWAMP` session."""
     extra_vars = {
         "subscription": json.loads(json_dumps(subscription)),
@@ -64,32 +68,40 @@ def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP",
     }
 
-    inventory = (
-        f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}"
-        f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}"
-    )
-
-    execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars)
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "deploy_twamp.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check TWAMP status on both sides")
-def check_twamp_status(subscription: Iptrunk, callback_route: str) -> State:
+def check_twamp_status(subscription: Iptrunk) -> LSOState:
     """Check TWAMP session."""
     extra_vars = {
         "subscription": json.loads(json_dumps(subscription)),
         "verb": "check_twamp",
     }
 
-    inventory = (
-        f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}"
-        f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}"
-    )
-
-    execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars)
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "deploy_twamp.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @workflow(
diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index c7021875a1d7f2e2fde5424b5493de028b209d58..3c04fb8e03213eb218f6fc0f230b1b5b504343e5 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -28,7 +28,7 @@ from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock, IptrunkTy
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
 from gso.services import infoblox
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.services.sharepoint import SharePointClient
 from gso.services.subscriptions import get_active_router_subscriptions
@@ -203,29 +203,28 @@ def calculate_old_side_data(subscription: Iptrunk, replace_index: int) -> State:
 
 
 @step("Check Optical PRE levels on the trunk endpoint")
-def check_ip_trunk_optical_levels_pre(subscription: Iptrunk, callback_route: str) -> State:
+def check_ip_trunk_optical_levels_pre(subscription: Iptrunk) -> LSOState:
     """Check Optical PRE levels on the trunk."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_pre"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check Optical POST levels on the trunk endpoint")
 def check_ip_trunk_optical_levels_post(
-    subscription: Iptrunk,
-    callback_route: str,
-    new_node: Router,
-    new_lag_member_interfaces: list[dict],
-    replace_index: int,
-) -> State:
+    subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int
+) -> LSOState:
     """Check Optical POST levels on the trunk."""
     extra_vars = {
         "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
@@ -235,25 +234,24 @@ def check_ip_trunk_optical_levels_post(
         "check": "optical_post",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check LLDP on the trunk endpoints")
 def check_ip_trunk_lldp(
-    subscription: Iptrunk,
-    callback_route: str,
-    new_node: Router,
-    new_lag_member_interfaces: list[dict],
-    replace_index: int,
-) -> State:
+    subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int
+) -> LSOState:
     """Check LLDP on the new trunk endpoints."""
     extra_vars = {
         "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
@@ -263,28 +261,30 @@ def check_ip_trunk_lldp(
         "check": "lldp",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Disable configuration on old router")
 def disable_old_config_dry(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Perform a dry run of disabling the old configuration on the routers."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -299,29 +299,31 @@ def disable_old_config_dry(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Disable configuration on old router")
 def disable_old_config_real(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Disable old configuration on the routers."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -336,31 +338,31 @@ def disable_old_config_real(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
     return {
-        "subscription": subscription,
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
     }
 
 
 @step("[DRY RUN] Deploy configuration on new router")
 def deploy_new_config_dry(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Perform a dry run of deploying configuration on the new router."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -375,29 +377,31 @@ def deploy_new_config_dry(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deploy configuration on new router")
 def deploy_new_config_real(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Deploy configuration on the new router."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -412,27 +416,25 @@ def deploy_new_config_real(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Update BFD on the remaining side")
 def update_remaining_side_bfd_dry(
-    subscription: Iptrunk,
-    callback_route: str,
-    new_node: Router,
-    replace_index: int,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+    subscription: Iptrunk, new_node: Router, replace_index: int, process_id: UUIDstr, tt_number: str
+) -> LSOState:
     """Perform a dry run of updating configuration on the remaining router."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -444,25 +446,23 @@ def update_remaining_side_bfd_dry(
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Update BFD on the remaining side")
 def update_remaining_side_bfd_real(
-    subscription: Iptrunk,
-    callback_route: str,
-    new_node: Router,
-    replace_index: int,
-    process_id: UUIDstr,
-    tt_number: str,
-) -> State:
+    subscription: Iptrunk, new_node: Router, replace_index: int, process_id: UUIDstr, tt_number: str
+) -> LSOState:
     """Update configuration on the remaining router."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -474,23 +474,21 @@ def update_remaining_side_bfd_real(
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check BFD session over trunk")
-def check_ip_trunk_bfd(
-    subscription: Iptrunk,
-    callback_route: str,
-    new_node: Router,
-    replace_index: int,
-) -> State:
+def check_ip_trunk_bfd(subscription: Iptrunk, new_node: Router, replace_index: int) -> LSOState:
     """Check BFD session across the new trunk."""
     extra_vars = {
         "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
@@ -498,14 +496,17 @@ def check_ip_trunk_bfd(
         "check": "bfd",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM)
@@ -523,35 +524,33 @@ def confirm_continue_move_fiber() -> FormGenerator:
 
 
 @step("Check IP connectivity of the trunk")
-def check_ip_trunk_connectivity(
-    subscription: Iptrunk,
-    callback_route: str,
-    replace_index: int,
-) -> State:
+def check_ip_trunk_connectivity(subscription: Iptrunk, replace_index: int) -> LSOState:
     """Check successful connectivity across the new trunk."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deploy ISIS configuration on new router")
 def deploy_new_isis(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Deploy :term:`ISIS` configuration."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -566,35 +565,37 @@ def deploy_new_isis(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check ISIS adjacency")
-def check_ip_trunk_isis(
-    subscription: Iptrunk,
-    callback_route: str,
-    replace_index: int,
-) -> State:
+def check_ip_trunk_isis(subscription: Iptrunk, replace_index: int) -> LSOState:
     """Run an Ansible playbook to confirm :term:`ISIS` adjacency."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "isis"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM)
@@ -615,10 +616,9 @@ def confirm_continue_restore_isis() -> FormGenerator:
 def restore_isis_metric(
     subscription: Iptrunk,
     process_id: UUIDstr,
-    callback_route: str,
     tt_number: str,
     old_isis_metric: int,
-) -> State:
+) -> LSOState:
     """Restore the :term:`ISIS` metric to its original value."""
     subscription.iptrunk.iptrunk_isis_metric = old_isis_metric
     extra_vars = {
@@ -630,28 +630,30 @@ def restore_isis_metric(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Delete configuration on old router")
 def delete_old_config_dry(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Perform a dry run of deleting the old configuration."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -666,29 +668,31 @@ def delete_old_config_dry(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Delete configuration on old router")
 def delete_old_config_real(
     subscription: Iptrunk,
-    callback_route: str,
     new_node: Router,
     new_lag_interface: str,
     new_lag_member_interfaces: list[dict],
     replace_index: int,
     process_id: UUIDstr,
     tt_number: str,
-) -> State:
+) -> LSOState:
     """Delete old configuration from the routers."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -703,16 +707,19 @@ def delete_old_config_real(
         f"- Deploy config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks_migration.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n"
-        f"{new_node.router.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_migration.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                    new_node.router.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Update IP records in IPAM")
diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py
index da14d078cea88f0c52256e02ef3dce0429cf7374..d95a6eb633000cce5238e044d747743b77295f8a 100644
--- a/gso/workflows/iptrunk/modify_isis_metric.py
+++ b/gso/workflows/iptrunk/modify_isis_metric.py
@@ -11,7 +11,7 @@ from orchestrator.workflows.steps import resync, store_process_subscription, uns
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_types.iptrunk import Iptrunk
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.utils.types.tt_number import TTNumber
 
 
@@ -37,12 +37,7 @@ def modify_iptrunk_subscription(subscription: Iptrunk, isis_metric: int) -> Stat
 
 
 @step("[DRY RUN] Provision IP trunk ISIS interface")
-def provision_ip_trunk_isis_iface_dry(
-    subscription: Iptrunk,
-    process_id: UUIDstr,
-    callback_route: str,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_isis_iface_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Perform a dry run of deploying the new :term:`ISIS` metric on both sides of the trunk."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -53,24 +48,22 @@ def provision_ip_trunk_isis_iface_dry(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Provision IP trunk ISIS interface")
-def provision_ip_trunk_isis_iface_real(
-    subscription: Iptrunk,
-    process_id: UUIDstr,
-    callback_route: str,
-    tt_number: str,
-) -> State:
+def provision_ip_trunk_isis_iface_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Deploy the new :term:`ISIS` metric on both sides of the trunk."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -81,15 +74,18 @@ def provision_ip_trunk_isis_iface_real(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @workflow(
diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py
index 6d82090c56d539c3456344ec6c08b1580d33dcf4..0dcc5d83cafecd34161ca2a2b272804c35e85a0b 100644
--- a/gso/workflows/iptrunk/modify_trunk_interface.py
+++ b/gso/workflows/iptrunk/modify_trunk_interface.py
@@ -21,7 +21,7 @@ from gso.products.product_blocks.iptrunk import (
     IptrunkType,
 )
 from gso.products.product_types.iptrunk import Iptrunk
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.utils.helpers import (
     available_interfaces_choices,
@@ -186,34 +186,63 @@ def determine_change_in_capacity(
 
 
 @step("Check IP connectivity of the trunk")
-def check_ip_trunk_connectivity(subscription: Iptrunk, callback_route: str) -> State:
+def check_ip_trunk_connectivity(subscription: Iptrunk) -> LSOState:
     """Check successful connectivity across a trunk."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn,
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Check LLDP on the trunk endpoints")
-def check_ip_trunk_lldp(subscription: Iptrunk, callback_route: str) -> State:
+def check_ip_trunk_lldp(subscription: Iptrunk) -> LSOState:
     """Check LLDP on trunk endpoints."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "lldp"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
-    return {"subscription": subscription}
+
+def update_side_members(subscription: Iptrunk, side_index: int, new_members: list[dict]) -> None:
+    """Update the AE members for a given side without removing unchanged members."""
+    # Prepare a dictionary for quick lookup of existing members by name
+    current_members = subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_ae_members
+    existing_members_dict = {member.interface_name: member for member in current_members}
+
+    # Iterate over new members and update or add them
+    for new_member in new_members:
+        interface_name = new_member["interface_name"]
+        if interface_name in existing_members_dict:
+            # Member exists, update details but keep the same subscription ID
+            existing_member = existing_members_dict[interface_name]
+            existing_member.interface_description = new_member["interface_description"]
+        else:
+            # New member, create a new subscription ID
+            current_members.append(IptrunkInterfaceBlock.new(subscription_id=uuid4(), **new_member))
+
+    # Remove members that are no longer in the new members list
+    subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_ae_members = [
+        member for member in current_members if member.interface_name in [m["interface_name"] for m in new_members]
+    ]
 
 
 @step("Update subscription")
@@ -242,12 +271,16 @@ def modify_iptrunk_subscription(
         for side in subscription.iptrunk.iptrunk_sides
     ]
     removed_ae_members = []
-
+    # Compare previous and current members to determine which ones were removed
     for side_index in range(2):
         previous_members = previous_ae_members[side_index]
         current_members = side_a_ae_members if side_index == 0 else side_b_ae_members
-        removed_ae_members.append([ae_member for ae_member in previous_members if ae_member not in current_members])
-
+        removed_ae_members.append([
+            ae_member
+            for ae_member in previous_members
+            if ae_member["interface_name"] not in [m["interface_name"] for m in current_members]
+        ])
+    # Update the subscription
     subscription.iptrunk.geant_s_sid = geant_s_sid
     subscription.iptrunk.iptrunk_description = iptrunk_description
     subscription.iptrunk.iptrunk_type = iptrunk_type
@@ -255,20 +288,9 @@ def modify_iptrunk_subscription(
     subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
 
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid = side_a_ae_geant_a_sid
-    #  Flush the old list of member interfaces
-    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members.clear()
-    #  And update the list to only include the new member interfaces
-    for member in side_a_ae_members:
-        subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members.append(
-            IptrunkInterfaceBlock.new(subscription_id=uuid4(), **member),
-        )
-
+    update_side_members(subscription, 0, side_a_ae_members)
     subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_geant_a_sid = side_b_ae_geant_a_sid
-    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_members.clear()
-    for member in side_b_ae_members:
-        subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_members.append(
-            IptrunkInterfaceBlock.new(subscription_id=uuid4(), **member),
-        )
+    update_side_members(subscription, 1, side_b_ae_members)
 
     side_names = sorted([
         subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_site.site_name,
@@ -285,12 +307,8 @@ def modify_iptrunk_subscription(
 
 @step("[DRY RUN] Provision IP trunk interface")
 def provision_ip_trunk_iface_dry(
-    subscription: Iptrunk,
-    process_id: UUIDstr,
-    callback_route: str,
-    tt_number: str,
-    removed_ae_members: list[str],
-) -> State:
+    subscription: Iptrunk, process_id: UUIDstr, tt_number: str, removed_ae_members: list[str]
+) -> LSOState:
     """Perform a dry run of deploying the updated IP trunk."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -302,25 +320,24 @@ def provision_ip_trunk_iface_dry(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Provision IP trunk interface")
 def provision_ip_trunk_iface_real(
-    subscription: Iptrunk,
-    process_id: UUIDstr,
-    callback_route: str,
-    tt_number: str,
-    removed_ae_members: list[str],
-) -> State:
+    subscription: Iptrunk, process_id: UUIDstr, tt_number: str, removed_ae_members: list[str]
+) -> LSOState:
     """Provision the new IP trunk with updated interfaces."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -332,15 +349,18 @@ def provision_ip_trunk_iface_real(
         f"{subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 def _netbox_update_interfaces(
@@ -439,19 +459,22 @@ def allocate_interfaces_in_netbox_side_b(subscription: Iptrunk, previous_ae_memb
 
 
 @step("Check Optical POST levels on the trunk endpoint")
-def check_ip_trunk_optical_levels_post(subscription: Iptrunk, callback_route: str) -> State:
+def check_ip_trunk_optical_levels_post(subscription: Iptrunk) -> LSOState:
     """Check Optical POST levels on the trunk."""
     extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_post"}
 
-    execute_playbook(
-        playbook_name="iptrunks_checks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks_checks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @workflow(
diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py
index ac628d393a9c6dd52880ee28ceb39d403f023fd2..549bc3c23c6de32a8a8cb1cf8f1fd667aea9813b 100644
--- a/gso/workflows/iptrunk/terminate_iptrunk.py
+++ b/gso/workflows/iptrunk/terminate_iptrunk.py
@@ -20,7 +20,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from gso.products.product_blocks.iptrunk import IptrunkSideBlock
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services import infoblox
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.utils.helpers import get_router_vendor
 from gso.utils.shared_enums import Vendor
@@ -51,7 +51,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
 
 @step("[DRY RUN] Deprovision IP trunk")
-def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
+def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Perform a dry run of deleting configuration from the routers."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -62,19 +62,22 @@ def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callbac
         f"- Remove config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-        callback_route=callback_route,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deprovision IP trunk")
-def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
+def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Delete configuration from the routers."""
     extra_vars = {
         "wfo_trunk_json": json.loads(json_dumps(subscription)),
@@ -85,15 +88,18 @@ def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callba
         f"- Remove config for {subscription.iptrunk.geant_s_sid}",
     }
 
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-        callback_route=callback_route,
-    )
-
-    return {"subscription": subscription}
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 def _free_up_interfaces_from_netbox(side_block: IptrunkSideBlock) -> None:
diff --git a/gso/workflows/iptrunk/validate_iptrunk.py b/gso/workflows/iptrunk/validate_iptrunk.py
index bdfc4d3b952eca2e998d46ceab47250a56cfdb21..7832487fba8cddcbef46247ed8ac85f0cc5a3dd1 100644
--- a/gso/workflows/iptrunk/validate_iptrunk.py
+++ b/gso/workflows/iptrunk/validate_iptrunk.py
@@ -11,24 +11,29 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services import infoblox
-from gso.services.lso_client import anonymous_lso_interaction, execute_playbook
+from gso.services.lso_client import LSOState, anonymous_lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.utils.helpers import get_router_vendor
 from gso.utils.shared_enums import Vendor
 
 
 @step("Validate IP trunk configuration")
-def validate_router_config(subscription: Iptrunk, callback_route: str) -> None:
+def validate_router_config(subscription: Iptrunk) -> LSOState:
     """Run an Ansible playbook that validates the configuration that is present on an active IP trunk."""
     extra_vars = {"wfo_trunk_json": json.loads(json_dumps(subscription)), "verb": "validate"}
 
-    execute_playbook(
-        playbook_name="base_config.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "base_config.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Verify IPAM resources for LAG interfaces")
@@ -129,56 +134,71 @@ def verify_netbox_entries(subscription: Iptrunk) -> None:
 
 
 @step("Verify configuration of IPtrunk")
-def verify_iptrunk_config(subscription: Iptrunk, callback_route: str) -> None:
+def verify_iptrunk_config(subscription: Iptrunk) -> LSOState:
     """Check for configuration drift on the relevant routers."""
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars={
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": {
             "wfo_trunk_json": json.loads(json_dumps(subscription)),
             "verb": "deploy",
             "dry_run": "true",
             "config_object": "trunk_interface",
             "is_verification_workflow": "true",
         },
-    )
+    }
 
 
 @step("Check ISIS configuration")
-def check_ip_trunk_isis(subscription: Iptrunk, callback_route: str) -> None:
+def check_ip_trunk_isis(subscription: Iptrunk) -> LSOState:
     """Run an Ansible playbook to check for any :term:`ISIS` configuration drift."""
-    execute_playbook(
-        playbook_name="iptrunks.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars={
+    return {
+        "playbook_name": "iptrunks.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": {
             "wfo_trunk_json": json.loads(json_dumps(subscription)),
             "verb": "deploy",
             "dry_run": "true",
             "config_object": "isis_interface",
             "is_verification_workflow": "true",
         },
-    )
+    }
 
 
 @step("Verify TWAMP configuration")
-def verify_twamp_config(subscription: Iptrunk, callback_route: str) -> None:
+def verify_twamp_config(subscription: Iptrunk) -> LSOState:
     """Check for configuration drift of TWAMP."""
-    execute_playbook(
-        playbook_name="deploy_twamp.yaml",
-        callback_route=callback_route,
-        inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n"
-        f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n",
-        extra_vars={
+    return {
+        "playbook_name": "deploy_twamp.yaml",
+        "inventory": {
+            "all": {
+                "hosts": {
+                    subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None,
+                    subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None,
+                }
+            }
+        },
+        "extra_vars": {
             "subscription": json.loads(json_dumps(subscription)),
             "verb": "deploy",
             "dry_run": "true",
             "is_verification_workflow": "true",
         },
-    )
+    }
 
 
 @workflow(
diff --git a/gso/workflows/router/promote_p_to_pe.py b/gso/workflows/router/promote_p_to_pe.py
index 03d68f099a0a8a373c0d5435e5afdfc0d845112e..cdd6c9dbf809025992a19302a5d53b7aae0e1e72 100644
--- a/gso/workflows/router/promote_p_to_pe.py
+++ b/gso/workflows/router/promote_p_to_pe.py
@@ -17,9 +17,8 @@ from pydantic import ConfigDict, model_validator
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
-from gso.services import lso_client
 from gso.services.kentik_client import KentikClient, NewKentikDevice
-from gso.services.lso_client import lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.subscriptions import get_all_active_sites
 from gso.utils.helpers import generate_inventory_for_active_routers
 from gso.utils.shared_enums import Vendor
@@ -63,7 +62,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
 
 @step("Evacuate the router by setting isis_overload")
-def set_isis_overload(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def set_isis_overload(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Evacuate the router by setting isis overload."""
     extra_vars = {
         "dry_run": False,
@@ -72,18 +71,15 @@ def set_isis_overload(subscription: dict[str, Any], callback_route: str, tt_numb
         "verb": "set_isis_overload",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Deploy PE base config")
-def deploy_pe_base_config_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def deploy_pe_base_config_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of adding the base config to the router."""
     extra_vars = {
         "dry_run": True,
@@ -94,18 +90,15 @@ def deploy_pe_base_config_dry(
         "geant_sites": json.loads(json_dumps(get_all_active_sites())),
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deploy PE base config")
-def deploy_pe_base_config_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def deploy_pe_base_config_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of adding the base config to the router."""
     extra_vars = {
         "dry_run": False,
@@ -116,12 +109,11 @@ def deploy_pe_base_config_real(
         "geant_sites": json.loads(json_dumps(get_all_active_sites())),
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @inputstep("Prompt EARL insertion", assignee=Assignee.SYSTEM)
@@ -173,9 +165,7 @@ def create_kentik_device(subscription: Router) -> State:
 
 
 @step("[DRY RUN] Remove P from all PEs")
-def remove_p_from_pe_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def remove_p_from_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of removing the P router from all the PE routers."""
     extra_vars = {
         "dry_run": True,
@@ -185,18 +175,15 @@ def remove_p_from_pe_dry(
         "verb": "remove_p_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Remove P from all PEs")
-def remove_p_from_pe_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def remove_p_from_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Remove the P router from all the PE routers."""
     extra_vars = {
         "dry_run": False,
@@ -206,18 +193,16 @@ def remove_p_from_pe_real(
         "verb": "remove_p_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
+
 
 
 @step("[DRY RUN] Deploy routing instances")
-def deploy_routing_instances_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def deploy_routing_instances_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of deploying routing instances."""
     extra_vars = {
         "dry_run": True,
@@ -226,18 +211,15 @@ def deploy_routing_instances_dry(
         "verb": "deploy_routing_instances",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Deploy routing instances")
-def deploy_routing_instances_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def deploy_routing_instances_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of deploying routing instances."""
     extra_vars = {
         "dry_run": False,
@@ -246,18 +228,15 @@ def deploy_routing_instances_real(
         "verb": "deploy_routing_instances",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Remove ISIS overload")
-def remove_isis_overload(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def remove_isis_overload(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Remove ISIS overload."""
     extra_vars = {
         "dry_run": False,
@@ -266,12 +245,11 @@ def remove_isis_overload(
         "verb": "remove_isis_overload",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Set router role to PE (Update subscription model)")
@@ -282,10 +260,29 @@ def update_subscription_model(subscription: Router) -> State:
     return {"subscription": subscription}
 
 
+@step("[DRY RUN] Add all P to this new PE")
+def add_all_p_to_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
+    """Perform a dry run of adding all P routers to the PE router."""
+    extra_vars = {
+        "dry_run": True,
+        "subscription": subscription,
+        "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Add all P-routers to this new PE",
+        "verb": "add_all_p_to_pe",
+        "p_router_list": generate_inventory_for_active_routers(
+            RouterRole.P, exclude_routers=[subscription["router"]["router_fqdn"]]
+        )["all"]["hosts"],
+    }
+
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
+
+
+
 @step("[DRY RUN] Delete default routes")
-def delete_default_routes_dry(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def delete_default_routes_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of deleting the default routes."""
     extra_vars = {
         "dry_run": True,
@@ -295,18 +292,15 @@ def delete_default_routes_dry(
         "verb": "delete_default_routes",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Delete default routes")
-def delete_default_routes_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def delete_default_routes_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of deleting the default routes."""
     extra_vars = {
         "dry_run": False,
@@ -316,12 +310,11 @@ def delete_default_routes_real(
         "verb": "delete_default_routes",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="promote_p_to_pe.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "promote_p_to_pe.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @workflow(
diff --git a/gso/workflows/router/terminate_router.py b/gso/workflows/router/terminate_router.py
index f7caa6c9936154f3afff8320f5942504f9477414..13cef223c8dfd0160130064d8e29e2290aa54f75 100644
--- a/gso/workflows/router/terminate_router.py
+++ b/gso/workflows/router/terminate_router.py
@@ -22,10 +22,10 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
-from gso.services import infoblox, lso_client
+from gso.services import infoblox
 from gso.services.kentik_client import KentikClient
 from gso.services.librenms_client import LibreNMSClient
-from gso.services.lso_client import execute_playbook, lso_interaction
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.settings import load_oss_params
 from gso.utils.helpers import generate_inventory_for_active_routers
@@ -69,9 +69,7 @@ def deprovision_loopback_ips(subscription: Router) -> dict:
 
 
 @step("[DRY RUN] Remove configuration from router")
-def remove_config_from_router_dry(
-    subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str
-) -> None:
+def remove_config_from_router_dry(subscription: Router, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Remove configuration from the router, first as a dry run."""
     extra_vars = {
         "wfo_router_json": json.loads(json_dumps(subscription)),
@@ -81,18 +79,15 @@ def remove_config_from_router_dry(
         f"{subscription.router.router_fqdn}",
     }
 
-    execute_playbook(
-        playbook_name="base_config.yaml",
-        callback_route=callback_route,
-        inventory=subscription.router.router_fqdn,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "base_config.yaml",
+        "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Remove configuration from router")
-def remove_config_from_router_real(
-    subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str
-) -> None:
+def remove_config_from_router_real(subscription: Router, process_id: UUIDstr, tt_number: str) -> LSOState:
     """Remove configuration from the router."""
     extra_vars = {
         "wfo_router_json": json.loads(json_dumps(subscription)),
@@ -102,12 +97,11 @@ def remove_config_from_router_real(
         f"{subscription.router.router_fqdn}",
     }
 
-    execute_playbook(
-        playbook_name="base_config.yaml",  # FIX: need to use correct playbook.
-        callback_route=callback_route,
-        inventory=subscription.router.router_fqdn,
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "base_config.yaml",
+        "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Remove Device from Netbox")
@@ -118,7 +112,7 @@ def remove_device_from_netbox(subscription: Router) -> dict[str, Router]:
 
 
 @step("[DRY RUN] Remove P router from all the PE routers")
-def remove_p_from_all_pe_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_p_from_all_pe_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of removing the terminated router from all the PE routers."""
     extra_vars = {
         "dry_run": True,
@@ -128,16 +122,15 @@ def remove_p_from_all_pe_dry(subscription: Router, callback_route: str, tt_numbe
         "verb": "remove_p_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[REAL RUN] Remove P router from all the PE routers")
-def remove_p_from_all_pe_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_p_from_all_pe_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of removing the terminated router from all the PE routers."""
     extra_vars = {
         "dry_run": False,
@@ -147,16 +140,15 @@ def remove_p_from_all_pe_real(subscription: Router, callback_route: str, tt_numb
         "verb": "remove_p_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Remove PE router from all the PE routers")
-def remove_pe_from_all_pe_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_pe_from_all_pe_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of removing the terminated PE router from the PE router mesh."""
     extra_vars = {
         "dry_run": True,
@@ -166,18 +158,17 @@ def remove_pe_from_all_pe_dry(subscription: Router, callback_route: str, tt_numb
         "verb": "remove_pe_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(
             RouterRole.PE, exclude_routers=[subscription.router.router_fqdn]
         ),
-        extra_vars=extra_vars,
-    )
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[REAL RUN] Remove all PE routers from all the PE routers")
-def remove_pe_from_all_pe_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_pe_from_all_pe_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of removing terminated PE router from PE the router mesh."""
     extra_vars = {
         "dry_run": False,
@@ -187,18 +178,17 @@ def remove_pe_from_all_pe_real(subscription: Router, callback_route: str, tt_num
         "verb": "remove_pe_from_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(
             RouterRole.PE, exclude_routers=[subscription.router.router_fqdn]
         ),
-        extra_vars=extra_vars,
-    )
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Remove PE router from all the P routers")
-def remove_pe_from_all_p_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_pe_from_all_p_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of removing PE router from all P routers."""
     extra_vars = {
         "dry_run": True,
@@ -208,16 +198,15 @@ def remove_pe_from_all_p_dry(subscription: Router, callback_route: str, tt_numbe
         "verb": "remove_pe_from_p",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.P),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.P),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[REAL RUN] Remove PE router from all P routers")
-def remove_pe_from_all_p_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def remove_pe_from_all_p_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a real run of removing PE router from all P routers."""
     extra_vars = {
         "dry_run": False,
@@ -227,12 +216,11 @@ def remove_pe_from_all_p_real(subscription: Router, callback_route: str, tt_numb
         "verb": "remove_pe_from_p",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.P),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.P),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Remove Device from Librenms")
diff --git a/gso/workflows/router/update_ibgp_mesh.py b/gso/workflows/router/update_ibgp_mesh.py
index d436af6ea2081c5a0b3e9a9dae8305e0e4b900b9..98f17122773bfbac51a46b2ffa5feac3332cfe30 100644
--- a/gso/workflows/router/update_ibgp_mesh.py
+++ b/gso/workflows/router/update_ibgp_mesh.py
@@ -14,8 +14,8 @@ from pydantic import ConfigDict, model_validator
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
-from gso.services import librenms_client, lso_client
-from gso.services.lso_client import lso_interaction
+from gso.services import librenms_client
+from gso.services.lso_client import LSOState, lso_interaction
 from gso.services.subscriptions import get_trunks_that_terminate_on_router
 from gso.utils.helpers import generate_inventory_for_active_routers
 from gso.utils.types.snmp import SNMPVersion
@@ -64,13 +64,11 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
     user_input = yield AddBGPSessionForm
 
-    return user_input.model_dump() | {
-        "router_role": subscription.router.router_role,
-    }
+    return user_input.model_dump()
 
 
 @step("[DRY RUN] Add P router to iBGP mesh")
-def add_p_to_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def add_p_to_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Perform a dry run of adding the new P router to the PE router mesh."""
     extra_vars = {
         "dry_run": True,
@@ -79,16 +77,15 @@ def add_p_to_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_numb
         "verb": "add_p_to_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Add P router to iBGP mesh")
-def add_p_to_mesh_real(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None:
+def add_p_to_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Add the P router to the mesh of PE routers."""
     extra_vars = {
         "dry_run": False,
@@ -97,16 +94,15 @@ def add_p_to_mesh_real(subscription: dict[str, Any], callback_route: str, tt_num
         "verb": "add_p_to_pe",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=generate_inventory_for_active_routers(RouterRole.PE),
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": generate_inventory_for_active_routers(RouterRole.PE),
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[DRY RUN] Add all PE routers to P router iBGP group")
-def add_all_pe_to_p_dry(subscription: dict[str, Any], callback_route: str) -> None:
+def add_all_pe_to_p_dry(subscription: dict[str, Any]) -> LSOState:
     """Perform a dry run of adding the list of all PE routers to the new P router."""
     extra_vars = {
         "dry_run": True,
@@ -115,18 +111,15 @@ def add_all_pe_to_p_dry(subscription: dict[str, Any], callback_route: str) -> No
         "verb": "add_pe_to_p",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("[FOR REAL] Add all PE routers to P router iBGP group")
-def add_all_pe_to_p_real(
-    subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr
-) -> None:
+def add_all_pe_to_p_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> LSOState:
     """Add the list of all PE routers to the new P router."""
     extra_vars = {
         "dry_run": False,
@@ -136,23 +129,21 @@ def add_all_pe_to_p_real(
         "verb": "add_pe_to_p",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Verify iBGP session health")
-def check_ibgp_session(subscription: Router, callback_route: str) -> None:
+def check_ibgp_session(subscription: Router) -> LSOState:
     """Run a playbook using the provisioning proxy, to check the health of the new iBGP session."""
-    lso_client.execute_playbook(
-        playbook_name="check_ibgp.yaml",
-        callback_route=callback_route,
-        inventory=subscription.router.router_fqdn,
-        extra_vars={},
-    )
+    return {
+        "playbook_name": "check_ibgp.yaml",
+        "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}},
+        "extra_vars": {},
+    }
 
 
 @step("Add the router to LibreNMS")
diff --git a/gso/workflows/router/validate_router.py b/gso/workflows/router/validate_router.py
index aa22b773d6004862aaab0b8c7ec2bf08b89f24be..9e13f521cb4edb062086b696846ff3d8bc17d8f9 100644
--- a/gso/workflows/router/validate_router.py
+++ b/gso/workflows/router/validate_router.py
@@ -1,11 +1,9 @@
 """Router validation workflow. Used in a nightly schedule."""
 
-import json
 from typing import Any
 
 from orchestrator.targets import Target
 from orchestrator.utils.errors import ProcessFailureError
-from orchestrator.utils.json import json_dumps
 from orchestrator.workflow import StepList, begin, conditional, done, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
@@ -13,10 +11,10 @@ from pydantic_forms.types import State, UUIDstr
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
-from gso.services import infoblox, lso_client
+from gso.services import infoblox
 from gso.services.kentik_client import KentikClient
 from gso.services.librenms_client import LibreNMSClient
-from gso.services.lso_client import anonymous_lso_interaction, execute_playbook
+from gso.services.lso_client import LSOState, anonymous_lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.utils.helpers import generate_inventory_for_active_routers
 from gso.utils.shared_enums import Vendor
@@ -55,7 +53,7 @@ def check_netbox_entry_exists(subscription: Router) -> None:
 
 
 @step("Verify BGP configuration on P router")
-def verify_p_ibgp(subscription: dict[str, Any], callback_route: str) -> None:
+def verify_p_ibgp(subscription: dict[str, Any]) -> LSOState:
     """Perform a dry run of adding the list of all PE routers to the new P router."""
     extra_vars = {
         "dry_run": True,
@@ -65,12 +63,11 @@ def verify_p_ibgp(subscription: dict[str, Any], callback_route: str) -> None:
         "is_verification_workflow": "true",
     }
 
-    lso_client.execute_playbook(
-        playbook_name="update_ibgp_mesh.yaml",
-        callback_route=callback_route,
-        inventory=subscription["router"]["router_fqdn"],
-        extra_vars=extra_vars,
-    )
+    return {
+        "playbook_name": "update_ibgp_mesh.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": extra_vars,
+    }
 
 
 @step("Verify correct LibreNMS entry")
@@ -100,19 +97,18 @@ def check_kentik_entry_exists(subscription: Router) -> None:
 
 
 @step("Check base config for drift")
-def verify_base_config(subscription: Router, callback_route: str) -> None:
+def verify_base_config(subscription: dict[str, Any]) -> LSOState:
     """Workflow step for running a playbook that checks whether base config has drifted."""
-    execute_playbook(
-        playbook_name="base_config.yaml",
-        callback_route=callback_route,
-        inventory=subscription.router.router_fqdn,
-        extra_vars={
-            "wfo_router_json": json.loads(json_dumps(subscription)),
+    return {
+        "playbook_name": "base_config.yaml",
+        "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}},
+        "extra_vars": {
+            "wfo_router_json": subscription,
             "verb": "deploy",
             "dry_run": "true",
             "is_verification_workflow": "true",
         },
-    )
+    }
 
 
 @workflow(
diff --git a/requirements.txt b/requirements.txt
index 632c74b3af3c83c1ea442da2e4bf11530f682e7b..433a9e17894886e0b2648cebae089f677c3b0a87 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-orchestrator-core==2.7.4
+orchestrator-core==2.7.5
 requests==2.31.0
 infoblox-client~=0.6.0
 pycountry==23.12.11
diff --git a/setup.py b/setup.py
index 32cc5f5ce3450f379d0c47548a799683977205d5..840d33679d74054f70199c0537e6b780fb23bbec 100644
--- a/setup.py
+++ b/setup.py
@@ -4,14 +4,14 @@ from setuptools import find_packages, setup
 
 setup(
     name="geant-service-orchestrator",
-    version="2.15",
+    version="2.19",
     author="GÉANT Orchestration and Automation Team",
     author_email="goat@geant.org",
     description="GÉANT Service Orchestrator",
     url="https://gitlab.software.geant.org/goat/gap/geant-service-orchestrator",
     packages=find_packages(),
     install_requires=[
-        "orchestrator-core==2.7.4",
+        "orchestrator-core==2.7.5",
         "requests==2.31.0",
         "infoblox-client~=0.6.0",
         "pycountry==23.12.11",
diff --git a/start-worker.sh b/start-worker.sh
index cccd84bff48ebc3e7392e15847e8f898c5120ffb..92cd6304d95db2a697ca1b77e4e705c5b9350b1a 100755
--- a/start-worker.sh
+++ b/start-worker.sh
@@ -4,4 +4,4 @@ set -o errexit
 set -o nounset
 
 cd /app
-python -m celery -A gso.worker worker --loglevel=info --concurrency=1
+python -m celery -A gso.worker worker --loglevel=info --concurrency=1 --pool=solo
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index 0b7e00e638db87ddb27674d524bcbdde990d6067..267cd3827f7dbd908c472faf545e949fd231ee32 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -101,7 +101,7 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.create_host_by_ip")
@@ -162,7 +162,7 @@ def test_successful_iptrunk_creation_with_standard_lso_result(
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.find_host_by_ip")
@@ -199,7 +199,7 @@ def test_iptrunk_creation_fails_when_lso_return_code_is_one(
 
 @pytest.mark.parametrize("input_form_wizard_data", [Vendor.JUNIPER], indirect=True)
 @pytest.mark.workflow()
-@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network")
 @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.create_host_by_ip")
diff --git a/test/workflows/iptrunk/test_deploy_twamp.py b/test/workflows/iptrunk/test_deploy_twamp.py
index 8584cd99cae310bccf70ac22a6cd377b5672cefa..e65feac79a0a1fd9988eff99fe0bf2e55f68899b 100644
--- a/test/workflows/iptrunk/test_deploy_twamp.py
+++ b/test/workflows/iptrunk/test_deploy_twamp.py
@@ -12,7 +12,7 @@ from test.workflows import (
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.iptrunk.deploy_twamp.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 def test_iptrunk_deploy_twamp_success(
     mock_execute_playbook,
     iptrunk_subscription_factory,
diff --git a/test/workflows/iptrunk/test_modify_isis_metric.py b/test/workflows/iptrunk/test_modify_isis_metric.py
index 38f4b4e89e5d178b86c48eaafc32a311f07787c2..26a9bbd490eb4c73728a2e11defd836bf3701316 100644
--- a/test/workflows/iptrunk/test_modify_isis_metric.py
+++ b/test/workflows/iptrunk/test_modify_isis_metric.py
@@ -12,7 +12,7 @@ from test.workflows import (
 
 
 @pytest.mark.workflow()
-@patch("gso.services.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 def test_iptrunk_modify_isis_metric_success(
     mock_provision_ip_trunk,
     iptrunk_subscription_factory,
diff --git a/test/workflows/iptrunk/test_terminate_iptrunk.py b/test/workflows/iptrunk/test_terminate_iptrunk.py
index 7fcca946895247f644783eb83ed183f334e73321..7319596ffa58936b0f92e2f2dfc948619eb386fd 100644
--- a/test/workflows/iptrunk/test_terminate_iptrunk.py
+++ b/test/workflows/iptrunk/test_terminate_iptrunk.py
@@ -3,6 +3,7 @@ from unittest.mock import patch
 import pytest
 
 from gso.products import Iptrunk
+from gso.products.product_blocks.router import RouterRole
 from gso.settings import load_oss_params
 from test.services.conftest import MockedNetboxClient
 from test.workflows import (
@@ -14,8 +15,7 @@ from test.workflows import (
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.iptrunk.terminate_iptrunk.execute_playbook")
-@patch("gso.utils.workflow_steps.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.iptrunk.terminate_iptrunk.infoblox.delete_network")
 @patch("gso.services.netbox_client.NetboxClient.delete_interface")
 @patch("gso.services.netbox_client.NetboxClient.free_interface")
@@ -23,17 +23,20 @@ def test_successful_iptrunk_termination(
     mocked_free_interface,
     mocked_delete_interface,
     mock_infoblox_delete_network,
-    mock_set_isis_to_90k,
     mock_execute_playbook,
     iptrunk_subscription_factory,
     faker,
     data_config_filename,
+    nokia_router_subscription_factory,
 ):
     #  Set up mock return values
     product_id = iptrunk_subscription_factory()
     mocked_netbox = MockedNetboxClient()
     mocked_delete_interface.return_value = mocked_netbox.delete_interface()
     mocked_free_interface.return_value = mocked_netbox.free_interface()
+    #  Add two more routers to our fake network
+    nokia_router_subscription_factory(router_role=RouterRole.P)
+    nokia_router_subscription_factory(router_role=RouterRole.PE)
 
     #  Run workflow
     oss_params = load_oss_params()
@@ -60,7 +63,6 @@ def test_successful_iptrunk_termination(
     subscription = Iptrunk.from_subscription(subscription_id)
 
     assert subscription.status == "terminated"
-    assert mock_execute_playbook.call_count == 2
-    assert mock_set_isis_to_90k.call_count == 1
+    assert mock_execute_playbook.call_count == 3
     assert mock_infoblox_delete_network.call_count == 2
     assert subscription.iptrunk.iptrunk_isis_metric == oss_params.GENERAL.isis_high_metric
diff --git a/test/workflows/iptrunk/test_validate_iptrunk.py b/test/workflows/iptrunk/test_validate_iptrunk.py
index 13bcaed9b4be4284a6f1dc5fa4e41410d2f8dab6..6b132abca2e3b2616b2a9c8937fc5e64122837ba 100644
--- a/test/workflows/iptrunk/test_validate_iptrunk.py
+++ b/test/workflows/iptrunk/test_validate_iptrunk.py
@@ -69,7 +69,7 @@ def _mocked_netbox_client():
 @patch("gso.services.infoblox.find_network_by_cidr")
 @patch("gso.services.infoblox.find_v6_host_by_fqdn")
 @patch("gso.services.infoblox.find_host_by_fqdn")
-@patch("gso.workflows.iptrunk.validate_iptrunk.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.services.netbox_client.NetboxClient.get_interface_by_name_and_device")
 def test_validate_iptrunk_success(
     mock_get_interface_by_name,
@@ -219,7 +219,7 @@ def test_validate_iptrunk_success(
 @patch("gso.services.infoblox.find_network_by_cidr")
 @patch("gso.services.infoblox.find_v6_host_by_fqdn")
 @patch("gso.services.infoblox.find_host_by_fqdn")
-@patch("gso.workflows.iptrunk.validate_iptrunk.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.services.netbox_client.NetboxClient.get_interface_by_name_and_device")
 def test_validate_iptrunk_skip_legacy_trunks(
     mock_get_interface_by_name,
diff --git a/test/workflows/router/test_create_router.py b/test/workflows/router/test_create_router.py
index 83a2e53354eb202e44a665125d154245b4cb826e..a6bf456f3ef249d52817b2381c9cc3fcd40a5c86 100644
--- a/test/workflows/router/test_create_router.py
+++ b/test/workflows/router/test_create_router.py
@@ -36,7 +36,7 @@ def router_creation_input_form_data(site_subscription_factory, faker):
 
 
 @pytest.mark.workflow()
-@patch("gso.utils.workflow_steps.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.router.create_router.NetboxClient.create_device")
 @patch("gso.workflows.router.create_router.infoblox.hostname_available")
 @patch("gso.workflows.router.create_router.infoblox.find_host_by_fqdn")
@@ -117,7 +117,7 @@ def test_create_nokia_router_success(
 
 
 @pytest.mark.workflow()
-@patch("gso.utils.workflow_steps.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.router.create_router.NetboxClient.create_device")
 @patch("gso.workflows.router.create_router.infoblox.hostname_available")
 @patch("gso.workflows.router.create_router.infoblox.find_network_by_cidr")
diff --git a/test/workflows/router/test_promote_p_to_pe.py b/test/workflows/router/test_promote_p_to_pe.py
index 3bd7fdca1a65e0ad7bd0e1df59883cd020e9aca0..5e14dcaffb510fea4cee70f8672e1372c3634e7f 100644
--- a/test/workflows/router/test_promote_p_to_pe.py
+++ b/test/workflows/router/test_promote_p_to_pe.py
@@ -17,7 +17,7 @@ from test.workflows import (
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.router.promote_p_to_pe.KentikClient")
 def test_promote_p_to_pe_success(
     mock_kentik_client,
@@ -29,6 +29,9 @@ def test_promote_p_to_pe_success(
     """Test the successful promotion of a Nokia P router to a PE router."""
     mock_kentik_client.return_value = MockedKentikClient
     router_id = nokia_router_subscription_factory(router_role=RouterRole.P, status=SubscriptionLifecycle.ACTIVE)
+    #  Add two more routers to our fake network
+    nokia_router_subscription_factory(router_role=RouterRole.P)
+    nokia_router_subscription_factory(router_role=RouterRole.PE)
     input_data = [{"subscription_id": router_id}, {"tt_number": faker.tt_number()}]
     result, process_stat, step_log = run_workflow("promote_p_to_pe", input_data)
     for _ in range(3):
@@ -55,7 +58,7 @@ def test_promote_p_to_pe_juniper_router(juniper_router_subscription_factory, dat
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 def test_promote_p_to_pe_nokia_pe_router(
     mock_execute_playbook, nokia_router_subscription_factory, data_config_filename, faker
 ):
diff --git a/test/workflows/router/test_terminate_router.py b/test/workflows/router/test_terminate_router.py
index f2cb638ab1cf5f918184e218c0f2d31d4f65cc87..a5f4fd3d0176113eb3f4dce0ae8c5a6274a51dc0 100644
--- a/test/workflows/router/test_terminate_router.py
+++ b/test/workflows/router/test_terminate_router.py
@@ -30,6 +30,9 @@ def test_terminate_pe_router_full_success(
 ):
     #  Prepare mock values and expected results
     product_id = nokia_router_subscription_factory()
+    #  Add two more routers to our fake network
+    nokia_router_subscription_factory(router_role=RouterRole.P)
+    nokia_router_subscription_factory(router_role=RouterRole.PE)
     router_termination_input_form_data = {
         "tt_number": faker.tt_number(),
         "remove_configuration": remove_configuration,
@@ -81,6 +84,9 @@ def test_terminate_p_router_full_success(
 ):
     #  Prepare mock values and expected results
     product_id = nokia_router_subscription_factory(router_role=RouterRole.P)
+    #  Add two more routers to our fake network
+    nokia_router_subscription_factory(router_role=RouterRole.P)
+    nokia_router_subscription_factory(router_role=RouterRole.PE)
     router_termination_input_form_data = {
         "tt_number": faker.tt_number(),
         "remove_configuration": remove_configuration,
diff --git a/test/workflows/router/test_update_ibgp_mesh.py b/test/workflows/router/test_update_ibgp_mesh.py
index e0cf8209f876730dc15d2add15f7c3cae111d438..b473f2ea31c4b14f33343106535d1f52b79e3705 100644
--- a/test/workflows/router/test_update_ibgp_mesh.py
+++ b/test/workflows/router/test_update_ibgp_mesh.py
@@ -18,31 +18,21 @@ from test.workflows import (
 
 
 @pytest.mark.parametrize("trunk_status", [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE])
-@pytest.mark.parametrize("router_role", [RouterRole.P, RouterRole.PE])
 @pytest.mark.workflow()
-@patch("gso.workflows.router.update_ibgp_mesh.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.router.update_ibgp_mesh.librenms_client.LibreNMSClient.add_device")
 @patch("gso.workflows.router.update_ibgp_mesh.librenms_client.LibreNMSClient.device_exists")
 def test_update_ibgp_mesh_success(
     mock_librenms_device_exists,
     mock_librenms_add_device,
     mock_execute_playbook,
-    router_role,
     trunk_status,
     iptrunk_subscription_factory,
-    iptrunk_side_subscription_factory,
-    nokia_router_subscription_factory,
     data_config_filename,
     faker,
 ):
     mock_librenms_device_exists.return_value = False
-    router_a = nokia_router_subscription_factory(router_role=router_role)
-    router_b = nokia_router_subscription_factory(router_role=router_role)
-    side_a = iptrunk_side_subscription_factory(iptrunk_side_node=router_a)
-    side_b = iptrunk_side_subscription_factory(iptrunk_side_node=router_b)
-    ip_trunk = Iptrunk.from_subscription(
-        iptrunk_subscription_factory(status=trunk_status, iptrunk_sides=[side_a, side_b])
-    )
+    ip_trunk = Iptrunk.from_subscription(iptrunk_subscription_factory(status=trunk_status))
     ibgp_mesh_input_form_data = {
         "subscription_id": ip_trunk.iptrunk.iptrunk_sides[0].iptrunk_side_node.owner_subscription_id
     }
@@ -50,8 +40,7 @@ def test_update_ibgp_mesh_success(
         "update_ibgp_mesh", [ibgp_mesh_input_form_data, {"tt_number": faker.tt_number()}]
     )
 
-    lso_step_count = 5 if router_role == RouterRole.P else 14
-    for _ in range(lso_step_count):
+    for _ in range(5):
         result, step_log = assert_lso_interaction_success(result, process_stat, step_log)
 
     # Handle two consecutive user input steps
@@ -61,7 +50,7 @@ def test_update_ibgp_mesh_success(
 
     state = extract_state(result)
 
-    assert mock_execute_playbook.call_count == lso_step_count
+    assert mock_execute_playbook.call_count == 5
     assert mock_librenms_add_device.call_count == 1
     assert result.status == StepStatus.COMPLETE
     assert state["subscription"]["router"]["router_access_via_ts"] is False
diff --git a/test/workflows/router/test_validate_router.py b/test/workflows/router/test_validate_router.py
index 924577e23fa83471e1206a32ea554f5558733ab2..07fc78d74a8297f6d6b2a5f6d458aa1c6eeb5cab 100644
--- a/test/workflows/router/test_validate_router.py
+++ b/test/workflows/router/test_validate_router.py
@@ -15,7 +15,7 @@ from test.workflows import (
 
 @pytest.mark.workflow()
 @patch("gso.services.infoblox.find_host_by_fqdn")
-@patch("gso.services.lso_client.execute_playbook")
+@patch("gso.services.lso_client._send_request")
 @patch("gso.services.netbox_client.NetboxClient.get_device_by_name")
 @patch("gso.services.librenms_client.LibreNMSClient.validate_device")
 @patch("gso.services.kentik_client.KentikClient")