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..0e7414e785025c3291e9eeaf2651cce4c15ff454 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,4 +1,10 @@
 # Changelog
+
+## [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/services/kentik_client.py b/gso/services/kentik_client.py
index 95d55e22d53f056d08bd9376956808c9a11785c0..7849adeeec4f6347f2545c5ec76fffaf5f6ee0e1 100644
--- a/gso/services/kentik_client.py
+++ b/gso/services/kentik_client.py
@@ -109,7 +109,42 @@ class KentikClient:
         return {}
 
     def get_plans(self) -> list[dict[str, Any]]:
-        """Get all Kentik plans available."""
+        """Get all Kentik plans available.
+
+        Returns a list of ``plans`` that each have the following shape:
+
+        .. vale off
+        .. code-block:: json
+
+            "plan": {
+                "active": true,
+                "bgp_enabled": true,
+                "cdate" "1970-01-01T01:01:01.000Z",
+                "company_id": 111111,
+                "description": "A description of this plan",
+                "deviceTypes": [
+                    {"device_type": "router"},
+                    {"device_type": "host-nprobe-dns-www"}
+                ],
+                "devices": [
+                    {
+                        "id": "111111",
+                        "device_name": "rt0.city.tld.internal",
+                        "device_type": "router"
+                    },
+                ],
+                "edate": "2999-01-01T09:09:09.000Z",
+                "fast_retention": 10,
+                "full_retention": 5,
+                "id": 11111,
+                "max_bigdata_fps": 100,
+                "max_devices": 9001,
+                "max_fps": 200,
+                "name": "KENTIK-PLAN-01",
+                "metadata": {},
+            }
+        .. vale on
+        """
         return self._send_request("GET", "v5/plans")["plans"]
 
     def get_plan(self, plan_id: int) -> dict[str, Any]:
diff --git a/gso/services/lso_client.py b/gso/services/lso_client.py
index 6eca3f6559943df80660b3f71ba50e9c1feb1d64..77c2264798a009c79fb5a28dcd15a317e17b87c5 100644
--- a/gso/services/lso_client.py
+++ b/gso/services/lso_client.py
@@ -60,6 +60,7 @@ def execute_playbook(
     For example, an inventory consisting of two hosts, which each a unique host variable assigned to them looks as
     follows:
 
+    .. vale off
     .. code-block:: json
 
         "inventory": {
@@ -74,6 +75,7 @@ def execute_playbook(
                 }
             }
         }
+    .. vale on
 
     .. warning::
        Note the fact that the collection of all hosts is a dictionary, and not a list of strings. Ansible expects each
diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py
index 6d82090c56d539c3456344ec6c08b1580d33dcf4..e947fd52b4569506d452629b3d2a1fd547468ef1 100644
--- a/gso/workflows/iptrunk/modify_trunk_interface.py
+++ b/gso/workflows/iptrunk/modify_trunk_interface.py
@@ -216,6 +216,29 @@ def check_ip_trunk_lldp(subscription: Iptrunk, callback_route: str) -> State:
     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")
 def modify_iptrunk_subscription(
     subscription: Iptrunk,
@@ -242,12 +265,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 +282,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,
diff --git a/gso/workflows/router/modify_kentik_license.py b/gso/workflows/router/modify_kentik_license.py
index 28d9d21f945a55001f5b1601227f26c5494067ec..bd97b9c04605ed475fb7800a6cf965b5c3c895a1 100644
--- a/gso/workflows/router/modify_kentik_license.py
+++ b/gso/workflows/router/modify_kentik_license.py
@@ -22,7 +22,11 @@ logger = logging.getLogger()
 
 def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator:
     router = Router.from_subscription(subscription_id)
-    active_kentik_plans = {str(plan["id"]): plan["name"] for plan in KentikClient().get_plans() if plan["active"]}
+    active_kentik_plans = {
+        str(plan["id"]): f"{plan["name"]} - ({len(plan["devices"])}/{plan["max_devices"]})"
+        for plan in KentikClient().get_plans()
+        if plan["active"]
+    }
     available_kentik_plans = Choice(
         "Select a Kentik license",
         zip(active_kentik_plans.keys(), active_kentik_plans.items(), strict=True),  # type: ignore[arg-type]
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 aeee187a3057e09141b3c382836aa62f2a814c2f..dd65151d87623a0f976e4edb58cb05f4a473d593 100644
--- a/setup.py
+++ b/setup.py
@@ -4,14 +4,14 @@ from setuptools import find_packages, setup
 
 setup(
     name="geant-service-orchestrator",
-    version="2.14",
+    version="2.15",
     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",