diff --git a/Changelog.md b/Changelog.md
index caa750310b00321a244f49e80f2a236f51d27bc5..a696905e59c451aed5443afc340c8208a8071340 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,17 @@
 
 All notable changes to this project will be documented in this file.
 
+## [1.5] - 2024-04-22
+- Added deploy TWAMP functionality.
+- Made some changes on IP Trunk creation/modification workflow including:
+    - Replaced `minimum link` with `number of members` in the IP trunks workflows.
+    - Added a confirmation page for users to confirm the `minimum links` value.
+    - Made `interface description` optional.
+    - Made `GA-GID` and `GA-SID` optional.
+- Added a rule to OPA policies to give access to Inventory Provider.
+- Improved the codebase to include OIDC client ID in userinfo.
+- Added switch, LAN switch interconnect and Pop VLAN domain models. 
+
 ## [1.1] - 2024-04-04
 - Fixed the AttributeError in the migrate_iptrunk workflow.
 - Improved the delete device in Netbox functionality.
diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst
index 4844b8049b2fff9c98561ac6e9f56b92a63ef4ae..4dc66b8eac5e2e408f220c93b8c4be80cf8e3301 100644
--- a/docs/source/glossary.rst
+++ b/docs/source/glossary.rst
@@ -81,3 +81,6 @@ Glossary of terms
 
   WFO
     `Workflow Orchestrator <https://workfloworchestrator.org/>`_
+
+  LAN
+    Local Area Network
diff --git a/gso/api/v1/imports.py b/gso/api/v1/imports.py
index 0b2b6b1624c06acb657cca57ebb03a0817972cf0..643b4bc09a8295a35ca1b6f4ee1bcc08a978371e 100644
--- a/gso/api/v1/imports.py
+++ b/gso/api/v1/imports.py
@@ -62,7 +62,7 @@ class IptrunkImportModel(BaseModel):
     """Required fields for importing an existing :class:`gso.products.product_types.iptrunk`."""
 
     partner: str
-    geant_s_sid: str
+    geant_s_sid: str | None
     iptrunk_type: IptrunkType
     iptrunk_description: str
     iptrunk_speed: PhysicalPortCapacity
@@ -70,11 +70,11 @@ class IptrunkImportModel(BaseModel):
     iptrunk_isis_metric: int
     side_a_node_id: str
     side_a_ae_iface: str
-    side_a_ae_geant_a_sid: str
+    side_a_ae_geant_a_sid: str | None
     side_a_ae_members: list[LAGMember]
     side_b_node_id: str
     side_b_ae_iface: str
-    side_b_ae_geant_a_sid: str
+    side_b_ae_geant_a_sid: str | None
     side_b_ae_members: list[LAGMember]
 
     iptrunk_ipv4_network: ipaddress.IPv4Network
diff --git a/gso/auth/oidc_policy_helper.py b/gso/auth/oidc_policy_helper.py
index 241641dcfe3a93ffd41bb2962255c6557b95d235..1ba2eb3b8cdb3db63babe30beabf2c6186e3ae3c 100644
--- a/gso/auth/oidc_policy_helper.py
+++ b/gso/auth/oidc_policy_helper.py
@@ -92,6 +92,11 @@ class OIDCUserModel(dict):
                 return self.get(key)
             raise error from None
 
+    @property
+    def client_id(self) -> str:
+        """Return the client id."""
+        return self.get("client_id") or ""
+
     @property
     def user_name(self) -> str:
         """Return the username of the user."""
@@ -236,6 +241,8 @@ class OIDCUser(HTTPBearer):
 
             user_info = await self.userinfo(async_request, token)
 
+            user_info["client_id"] = intercepted_token.get("client_id")
+
             logger.debug("OIDCUserModel object.", intercepted_token=intercepted_token)
             return user_info
 
@@ -418,7 +425,7 @@ def opa_decision(
                 **(opa_kwargs or {}),
                 **user_info,
                 "resource": request.url.path,
-                "method": request_method,
+                "method": request_method.upper(),
                 "arguments": {"path": request.path_params, "query": {**request.query_params}, "json": json},
             }
         }
diff --git a/gso/migrations/versions/2024-04-17_393acfa175c0_add_switch_products.py b/gso/migrations/versions/2024-04-17_393acfa175c0_add_switch_products.py
new file mode 100644
index 0000000000000000000000000000000000000000..7444c84530978213d33a8fc8ec255f1b70c5c8c5
--- /dev/null
+++ b/gso/migrations/versions/2024-04-17_393acfa175c0_add_switch_products.py
@@ -0,0 +1,356 @@
+"""Add switch products..
+
+Revision ID: 393acfa175c0
+Revises:
+Create Date: 2024-04-17 15:09:13.716522
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '393acfa175c0'
+down_revision = None
+branch_labels = ('data',)
+depends_on = '4ec89ab289c0'
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+INSERT INTO products (name, description, product_type, tag, status) VALUES ('Switch', 'Switch', 'Switch', 'SWITCH', 'active') RETURNING products.product_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO products (name, description, product_type, tag, status) VALUES ('LAN Switch Interconnect', 'LAN Switch Interconnect', 'LanSwitchInterconnect', 'LSI', 'active') RETURNING products.product_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO products (name, description, product_type, tag, status) VALUES ('Pop VLAN', 'Pop VLAN', 'PopVlan', 'POP_VLAN', 'active') RETURNING products.product_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('SwitchBlock', 'SwitchBlock', 'SWITCH_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('LanSwitchInterconnectBlock', 'LanSwitchInterconnectBlock', 'LSI_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('LanSwitchInterconnectRouterSideBlock', 'LanSwitchInterconnectRouterSideBlock', 'LSI_RTR_SIDE_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectSwitchSideBlock', 'LSI_SW_SIDE_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('LanSwitchInterconnectInterfaceBlock', 'LanSwitchInterconnectInterfaceBlock', 'LSI_IFACE_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('PopVlanBlock', 'PopVlanBlock', 'POP_VLAN_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_blocks (name, description, tag, status) VALUES ('PopVlanPortBlock', 'PopVlanPortBlock', 'POP_VLAN_PORT_BLOCK', 'active') RETURNING product_blocks.product_block_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('switch_vendor', 'The vendor of the switch.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('vlan_id', 'The VLAN ID of the Pop VLAN.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('switch_model', 'The model of the switch.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('lan_switch_interconnect_description', 'Description') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('address_space', 'The address space of the VLAN Switch Interconnect. It can be private or public.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('switch_hostname', 'The hostname of the swirch') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('ipv4_network', 'IPv4 network for the Pop VLAN if layer_preference is L3.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('port_name', 'The port name') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('layer_preference', 'L2/L3') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('ae_iface', 'AE interface') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('ipv6_network', 'IPv6 network for the Pop VLAN if layer_preference is L3.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('port_description', 'The port description') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('minimum_links', 'The minimum amount of links') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('switch_ts_port', 'The port of the terminal server that this switch is connected to. Used to offer out of band access.') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('tagged', 'If the port is tagged') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('pop_vlan_description', 'POP VLAN description') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Switch')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('LAN Switch Interconnect')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Pop VLAN')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('RouterBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock'))), ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_hostname')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_ts_port')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_vendor')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_model')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_description')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_links')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ae_iface'))), ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ae_iface')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_name')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_description')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('pop_vlan_description')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('layer_preference')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_network')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_network')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_name')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_description')))
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('tagged')))
+    """))
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_hostname'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_hostname'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_ts_port'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_vendor'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_model'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_model'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_links'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_links'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ae_iface'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ae_iface'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_name'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_name'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('interface_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('pop_vlan_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('pop_vlan_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('layer_preference'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('layer_preference'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_network'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_network'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_network'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_network'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_name'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_name'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('port_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('tagged'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('tagged'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('switch_vendor', 'vlan_id', 'switch_model', 'lan_switch_interconnect_description', 'address_space', 'switch_hostname', 'ipv4_network', 'port_name', 'layer_preference', 'ae_iface', 'ipv6_network', 'port_description', 'minimum_links', 'switch_ts_port', 'tagged', 'pop_vlan_description'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM resource_types WHERE resource_types.resource_type IN ('switch_vendor', 'vlan_id', 'switch_model', 'lan_switch_interconnect_description', 'address_space', 'switch_hostname', 'ipv4_network', 'port_name', 'layer_preference', 'ae_iface', 'ipv6_network', 'port_description', 'minimum_links', 'switch_ts_port', 'tagged', 'pop_vlan_description')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Switch')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('LAN Switch Interconnect')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Pop VLAN')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('RouterBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectInterfaceBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PopVlanPortBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock', 'LanSwitchInterconnectInterfaceBlock', 'SwitchBlock', 'PopVlanPortBlock', 'LanSwitchInterconnectBlock', 'LanSwitchInterconnectSwitchSideBlock', 'PopVlanBlock'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock', 'LanSwitchInterconnectInterfaceBlock', 'SwitchBlock', 'PopVlanPortBlock', 'LanSwitchInterconnectBlock', 'LanSwitchInterconnectSwitchSideBlock', 'PopVlanBlock')
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Switch', 'LAN Switch Interconnect', 'Pop VLAN'))))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Switch', 'LAN Switch Interconnect', 'Pop VLAN')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Switch', 'LAN Switch Interconnect', 'Pop VLAN')))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Switch', 'LAN Switch Interconnect', 'Pop VLAN'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM products WHERE products.name IN ('Switch', 'LAN Switch Interconnect', 'Pop VLAN')
+    """))
diff --git a/gso/products/__init__.py b/gso/products/__init__.py
index 32350d65cc3a9b0e92e4757ffc8a7faf632f7379..cab8e801f207c7617c2e204cb9fdde0aa0d5a366 100644
--- a/gso/products/__init__.py
+++ b/gso/products/__init__.py
@@ -9,10 +9,13 @@ from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
 from pydantic_forms.types import strEnum
 
 from gso.products.product_types.iptrunk import Iptrunk
+from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
 from gso.products.product_types.office_router import OfficeRouter
+from gso.products.product_types.pop_vlan import PopVlan
 from gso.products.product_types.router import Router
 from gso.products.product_types.site import Site
 from gso.products.product_types.super_pop_switch import SuperPopSwitch
+from gso.products.product_types.switch import Switch
 
 
 class ProductName(strEnum):
@@ -23,6 +26,9 @@ class ProductName(strEnum):
     SITE = "Site"
     SUPER_POP_SWITCH = "Super PoP switch"
     OFFICE_ROUTER = "Office router"
+    SWITCH = "Switch"
+    LAN_SWITCH_INTERCONNECT = "LAN Switch Interconnect"
+    POP_VLAN = "Pop VLAN"
 
 
 class ProductType(strEnum):
@@ -33,6 +39,9 @@ class ProductType(strEnum):
     SITE = Site.__name__
     SUPER_POP_SWITCH = SuperPopSwitch.__name__
     OFFICE_ROUTER = OfficeRouter.__name__
+    SWITCH = Switch.__name__
+    LAN_SWITCH_INTERCONNECT = LanSwitchInterconnect.__name__
+    POP_VLAN = PopVlan.__name__
 
 
 SUBSCRIPTION_MODEL_REGISTRY.update(
@@ -42,5 +51,8 @@ SUBSCRIPTION_MODEL_REGISTRY.update(
         ProductName.SITE.value: Site,
         ProductName.SUPER_POP_SWITCH.value: SuperPopSwitch,
         ProductName.OFFICE_ROUTER.value: OfficeRouter,
+        ProductName.SWITCH.value: Switch,
+        ProductName.LAN_SWITCH_INTERCONNECT.value: LanSwitchInterconnect,
+        ProductName.POP_VLAN.value: PopVlan,
     },
 )
diff --git a/gso/products/product_blocks/iptrunk.py b/gso/products/product_blocks/iptrunk.py
index 901f37e787805e68307fc598a95080b0e6cdbd08..cac186262f641fc52fdc62a4e4c556c7d924f22b 100644
--- a/gso/products/product_blocks/iptrunk.py
+++ b/gso/products/product_blocks/iptrunk.py
@@ -55,14 +55,14 @@ class IptrunkInterfaceBlockProvisioning(IptrunkInterfaceBlockInactive, lifecycle
     """An IP trunk interface that is being provisioned."""
 
     interface_name: str
-    interface_description: str
+    interface_description: str | None = None
 
 
 class IptrunkInterfaceBlock(IptrunkInterfaceBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
     """An active IP trunk interface."""
 
     interface_name: str
-    interface_description: str
+    interface_description: str | None = None
 
 
 class IptrunkSides(UniqueConstrainedList[T_co]):  # type: ignore[type-var]
@@ -139,7 +139,7 @@ class IptrunkBlock(IptrunkBlockProvisioning, lifecycle=[SubscriptionLifecycle.AC
     """A trunk that's currently deployed in the network."""
 
     #:  GÉANT service ID associated with this trunk.
-    geant_s_sid: str
+    geant_s_sid: str | None = None
     #:  A human-readable description of this trunk.
     iptrunk_description: str
     #:  The type of trunk, can be either dark fibre or leased capacity.
diff --git a/gso/products/product_blocks/lan_switch_interconnect.py b/gso/products/product_blocks/lan_switch_interconnect.py
new file mode 100644
index 0000000000000000000000000000000000000000..55a7b97a3ca788dbe3e61f5ddd9c8a5344e5976f
--- /dev/null
+++ b/gso/products/product_blocks/lan_switch_interconnect.py
@@ -0,0 +1,149 @@
+"""LAN Switch Interconnect product block that has all parameters of a subscription throughout its lifecycle."""
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.types import SubscriptionLifecycle, strEnum
+
+from gso.products.product_blocks.iptrunk import LAGMemberList
+from gso.products.product_blocks.router import RouterBlock, RouterBlockInactive, RouterBlockProvisioning
+from gso.products.product_blocks.switch import SwitchBlock, SwitchBlockInactive, SwitchBlockProvisioning
+
+
+class LanSwitchInterconnectAddressSpace(strEnum):
+    """Types of LAN Switch Interconnect. Can be private or public."""
+
+    PRIVATE = "Private"
+    PUBLIC = "Public"
+
+
+class LanSwitchInterconnectInterfaceBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="LanSwitchInterconnectInterfaceBlock",
+):
+    """An inactive LAN Switch Interconnect interface."""
+
+    interface_name: str | None = None
+    interface_description: str | None = None
+
+
+class LanSwitchInterconnectInterfaceBlockProvisioning(
+    LanSwitchInterconnectInterfaceBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
+):
+    """A LAN Switch Interconnect interface that is being provisioned."""
+
+    interface_name: str
+    interface_description: str
+
+
+class LanSwitchInterconnectInterfaceBlock(
+    LanSwitchInterconnectInterfaceBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]
+):
+    """An active Switch Interconnect interface."""
+
+    interface_name: str
+    interface_description: str
+
+
+class LanSwitchInterconnectRouterSideBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="LanSwitchInterconnectRouterSideBlock",
+):
+    """An inactive LAN Switch Interconnect router side."""
+
+    node: RouterBlockInactive
+    ae_iface: str | None = None
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockInactive]
+
+
+class LanSwitchInterconnectRouterSideBlockProvisioning(
+    LanSwitchInterconnectRouterSideBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
+):
+    """An LAN Switch Interconnect router side that is being provisioned."""
+
+    node: RouterBlockProvisioning
+    ae_iface: str | None = None
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockProvisioning]
+
+
+class LanSwitchInterconnectRouterSideBlock(
+    LanSwitchInterconnectRouterSideBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]
+):
+    """An active LAN Switch Interconnect router side."""
+
+    node: RouterBlock
+    ae_iface: str
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlock]
+
+
+class LanSwitchInterconnectSwitchSideBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="LanSwitchInterconnectSwitchSideBlock",
+):
+    """An inactive LAN Switch Interconnect switch side."""
+
+    node: SwitchBlockInactive
+    ae_iface: str | None = None
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockInactive]
+
+
+class LanSwitchInterconnectSwitchSideBlockProvisioning(
+    LanSwitchInterconnectSwitchSideBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
+):
+    """An LAN Switch Interconnect switch side that is being provisioned."""
+
+    node: SwitchBlockProvisioning
+    ae_iface: str | None = None
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockProvisioning]
+
+
+class LanSwitchInterconnectSwitchSideBlock(
+    LanSwitchInterconnectSwitchSideBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]
+):
+    """An active LAN Switch Interconnect switch side."""
+
+    node: SwitchBlock
+    ae_iface: str
+    ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlock]
+
+
+class LanSwitchInterconnectBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="LanSwitchInterconnectBlock",
+):
+    """A LAN Switch Interconnect that's currently inactive, see :class:`LanSwitchInterconnectBlock`."""
+
+    lan_switch_interconnect_description: str | None = None
+    address_space: LanSwitchInterconnectAddressSpace | None = None
+    minimum_links: int | None = None
+    router_side: LanSwitchInterconnectRouterSideBlockInactive
+    switch_side: LanSwitchInterconnectSwitchSideBlockInactive
+
+
+class LanSwitchInterconnectBlockProvisioning(
+    LanSwitchInterconnectBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
+):
+    """A LAN Switch Interconnect that's currently being provisioned, see :class:`LanSwitchInterconnectBlock`."""
+
+    lan_switch_interconnect_description: str | None = None
+    address_space: LanSwitchInterconnectAddressSpace | None = None
+    minimum_links: int | None = None
+    router_side: LanSwitchInterconnectRouterSideBlockProvisioning
+    switch_side: LanSwitchInterconnectSwitchSideBlockProvisioning
+
+
+class LanSwitchInterconnectBlock(LanSwitchInterconnectBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A LAN Switch Interconnect that's currently deployed in the network."""
+
+    #:  A human-readable description of this LAN Switch Interconnect.
+    lan_switch_interconnect_description: str
+    #:  The address space of the VLAN Switch Interconnect. It can be private or public.
+    address_space: LanSwitchInterconnectAddressSpace
+    #:  The minimum amount of links the LAN Switch Interconnect should consist of.
+    minimum_links: int
+    #:  The router side of the LAN Switch Interconnect.
+    router_side: LanSwitchInterconnectRouterSideBlock
+    #:  The switch side of the LAN Switch Interconnect.
+    switch_side: LanSwitchInterconnectSwitchSideBlock
diff --git a/gso/products/product_blocks/pop_vlan.py b/gso/products/product_blocks/pop_vlan.py
new file mode 100644
index 0000000000000000000000000000000000000000..4935c2f69966874c686489c7e06e3061d5509365
--- /dev/null
+++ b/gso/products/product_blocks/pop_vlan.py
@@ -0,0 +1,101 @@
+"""Pop VLAN product block that has all parameters of a subscription throughout its lifecycle."""
+
+from ipaddress import IPv4Network, IPv6Network
+from typing import TypeVar
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.forms.validators import UniqueConstrainedList
+from orchestrator.types import SubscriptionLifecycle
+from pydantic_forms.types import strEnum
+
+from gso.products.product_blocks.lan_switch_interconnect import (
+    LanSwitchInterconnectBlock,
+    LanSwitchInterconnectBlockInactive,
+    LanSwitchInterconnectBlockProvisioning,
+)
+
+T_co = TypeVar("T_co", covariant=True)
+
+
+class LayerPreference(strEnum):
+    """Enumerator for the different types of layer preferences."""
+
+    L2 = "L2"
+    L3 = "L3"
+
+
+class PortList(UniqueConstrainedList[T_co]):  # type: ignore[type-var]
+    """A list of ports."""
+
+
+class PopVlanPortBlockInactive(
+    ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PopVlanPortBlock"
+):
+    """An inactive Pop VLAN port."""
+
+    port_name: str | None = None
+    port_description: str | None = None
+    tagged: bool | None = None
+
+
+class PopVlanPortBlockProvisioning(PopVlanPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A Pop VLAN port that is being provisioned."""
+
+    port_name: str | None = None
+    port_description: str | None = None
+    tagged: bool | None = None
+
+
+class PopVlanPortBlock(PopVlanPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """An active Pop VLAN port."""
+
+    port_name: str
+    port_description: str
+    tagged: bool
+
+
+class PopVlanBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="PopVlanBlock",
+):
+    """A Pop VLAN that's currently inactive, see :class:`PopVlanBlock`."""
+
+    vlan_id: int
+    pop_vlan_description: str | None
+    lan_switch_interconnect: LanSwitchInterconnectBlockInactive
+    ports: PortList[PopVlanPortBlockProvisioning]
+    layer_preference: LayerPreference
+    ipv4_network: IPv4Network | None = None
+    ipv6_network: IPv6Network | None = None
+
+
+class PopVlanBlockProvisioning(PopVlanBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A Pop VLAN that's currently being provisioned, see :class:`PopVlanBlock`."""
+
+    vlan_id: int
+    pop_vlan_description: str | None
+    lan_switch_interconnect: LanSwitchInterconnectBlockProvisioning
+    ports: PortList[PopVlanPortBlockProvisioning]
+    layer_preference: LayerPreference
+    ipv4_network: IPv4Network | None = None
+    ipv6_network: IPv6Network | None = None
+
+
+class PopVlanBlock(PopVlanBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A Pop VLAN that's currently deployed in the network."""
+
+    #: The VLAN ID of the Pop VLAN.
+    vlan_id: int
+    #: The description of the Pop VLAN.
+    pop_vlan_description: str
+    #: The LAN Switch Interconnect that this Pop VLAN is connected to.
+    lan_switch_interconnect: LanSwitchInterconnectBlock
+    #: The ports of the Pop VLAN.
+    ports: PortList[PopVlanPortBlock]
+    #: The level of the layer preference for the Pop VLAN (L2 or L3).
+    layer_preference: LayerPreference
+    #: IPv4 network for the Pop VLAN if layer preference is L3.
+    ipv4_network: IPv4Network | None = None
+    #: IPv6 network for the Pop VLAN if layer preference is L3.
+    ipv6_network: IPv6Network | None = None
diff --git a/gso/products/product_blocks/switch.py b/gso/products/product_blocks/switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..660cfd81320fc0739713a649a780b20265871d11
--- /dev/null
+++ b/gso/products/product_blocks/switch.py
@@ -0,0 +1,57 @@
+"""Product block for :class:`Switch` products."""
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.types import SubscriptionLifecycle
+from pydantic_forms.types import strEnum
+
+from gso.products.product_blocks.site import (
+    SiteBlock,
+    SiteBlockInactive,
+    SiteBlockProvisioning,
+)
+from gso.utils.shared_enums import PortNumber, Vendor
+
+
+class SwitchModel(strEnum):
+    """Enumerator for the different types of switches."""
+
+    EX3400 = "EX3400"
+
+
+class SwitchBlockInactive(
+    ProductBlockModel,
+    lifecycle=[SubscriptionLifecycle.INITIAL],
+    product_block_name="SwitchBlock",
+):
+    """A switch that's being currently inactive. See :class:`SwitchBlock`."""
+
+    switch_hostname: str | None = None
+    switch_ts_port: PortNumber | None = None
+    switch_site: SiteBlockInactive | None
+    switch_vendor: Vendor | None
+    switch_model: SwitchModel | None = None
+
+
+class SwitchBlockProvisioning(SwitchBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A switch that's being provisioned. See :class:`SwitchBlock`."""
+
+    switch_hostname: str
+    switch_ts_port: PortNumber
+    switch_site: SiteBlockProvisioning
+    switch_vendor: Vendor
+    switch_model: SwitchModel | None = None
+
+
+class SwitchBlock(SwitchBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A switch that's currently deployed in the network."""
+
+    #: The hostname of the switch.
+    switch_hostname: str
+    #:  The port of the terminal server that this switch is connected to. Used to offer out of band access.
+    switch_ts_port: PortNumber
+    #:  The :class:`Site` that this switch resides in. Both physically and computationally.
+    switch_site: SiteBlock
+    #: The vendor of the switch.
+    switch_vendor: Vendor
+    #: The model of the switch.
+    switch_model: SwitchModel | None = None
diff --git a/gso/products/product_types/lan_switch_interconnect.py b/gso/products/product_types/lan_switch_interconnect.py
new file mode 100644
index 0000000000000000000000000000000000000000..32433c4694b0f097bcdc80df742ca9695a9dee53
--- /dev/null
+++ b/gso/products/product_types/lan_switch_interconnect.py
@@ -0,0 +1,28 @@
+"""The product type for LAN Switch Interconnect."""
+
+from orchestrator.domain.base import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.lan_switch_interconnect import (
+    LanSwitchInterconnectBlock,
+    LanSwitchInterconnectBlockInactive,
+    LanSwitchInterconnectBlockProvisioning,
+)
+
+
+class LanSwitchInterconnectInactive(SubscriptionModel, is_base=True):
+    """An LAN Switch Interconnect that is inactive."""
+
+    lan_switch_interconnect: LanSwitchInterconnectBlockInactive
+
+
+class LanSwitchInterconnectProvisioning(LanSwitchInterconnectInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """An LAN Switch Interconnect that is being provisioned."""
+
+    lan_switch_interconnect: LanSwitchInterconnectBlockProvisioning
+
+
+class LanSwitchInterconnect(LanSwitchInterconnectProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """An LAN Switch Interconnect that is active."""
+
+    lan_switch_interconnect: LanSwitchInterconnectBlock
diff --git a/gso/products/product_types/pop_vlan.py b/gso/products/product_types/pop_vlan.py
new file mode 100644
index 0000000000000000000000000000000000000000..7567f4ea8014bbd49fa66b6204839d2596cccb30
--- /dev/null
+++ b/gso/products/product_types/pop_vlan.py
@@ -0,0 +1,24 @@
+"""The product type for Pop VLAN."""
+
+from orchestrator.domain.base import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.pop_vlan import PopVlanBlock, PopVlanBlockInactive, PopVlanBlockProvisioning
+
+
+class PopVlanInactive(SubscriptionModel, is_base=True):
+    """A Pop VLAN that is inactive."""
+
+    pop_vlan: PopVlanBlockInactive
+
+
+class PopVlanProvisioning(PopVlanInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A Pop VLAN that is being provisioned."""
+
+    pop_vlan: PopVlanBlockProvisioning
+
+
+class PopVlan(PopVlanProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A Pop VLAN that is active."""
+
+    pop_vlan: PopVlanBlock
diff --git a/gso/products/product_types/switch.py b/gso/products/product_types/switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef2eb05eac333628ba4ae5bef2424283adec3758
--- /dev/null
+++ b/gso/products/product_types/switch.py
@@ -0,0 +1,24 @@
+"""A switch product type."""
+
+from orchestrator.domain.base import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products.product_blocks.switch import SwitchBlock, SwitchBlockInactive, SwitchBlockProvisioning
+
+
+class SwitchInactive(SubscriptionModel, is_base=True):
+    """An inactive switch."""
+
+    switch: SwitchBlockInactive
+
+
+class SwitchProvisioning(SwitchInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
+    """A switch that is being provisioned."""
+
+    switch: SwitchBlockProvisioning
+
+
+class Switch(SwitchProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
+    """A switch that is currently active."""
+
+    switch: SwitchBlock
diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py
index ca01bcabb8727a92aa0a2912bcf142a2a550c3cd..06ee5719a48010c2cb175200abc3b8276312303d 100644
--- a/gso/services/infoblox.py
+++ b/gso/services/infoblox.py
@@ -19,10 +19,6 @@ class AllocationError(Exception):
     """Raised when Infoblox failed to allocate a resource."""
 
 
-class DeletionError(Exception):
-    """Raised when Infoblox failed to delete a resource."""
-
-
 def _setup_connection() -> tuple[connector.Connector, IPAMParams]:
     """Set up a new connection with an Infoblox instance.
 
@@ -158,7 +154,7 @@ def delete_network(ip_network: ipaddress.IPv4Network | ipaddress.IPv6Network) ->
         network.delete()
     else:
         msg = f"Could not find network {ip_network}, nothing has been deleted."
-        raise DeletionError(msg)
+        logger.warning(msg)
 
 
 def allocate_host(
@@ -253,8 +249,9 @@ def create_host_by_ip(
                         :term:`GSO`.
     """
     if not hostname_available(hostname):
-        msg = f"Cannot allocate new host, FQDN {hostname} already taken."
-        raise AllocationError(msg)
+        msg = f"FQDN {hostname} already taken, nothing to be done."
+        logger.warning(msg)
+        return
 
     conn, oss = _setup_connection()
     ipv6_object = objects.IP.create(ip=str(ipv6_address), mac=NULL_MAC, configure_for_dhcp=False)
@@ -331,7 +328,7 @@ def delete_host_by_ip(ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address) ->
         host.delete()
     else:
         msg = f"Could not find host at {ip_addr}, nothing has been deleted."
-        raise DeletionError(msg)
+        logger.warning(msg)
 
 
 def delete_host_by_fqdn(fqdn: str) -> None:
@@ -348,4 +345,4 @@ def delete_host_by_fqdn(fqdn: str) -> None:
         host.delete()
     else:
         msg = f"Could not find host at {fqdn}, nothing has been deleted."
-        raise DeletionError(msg)
+        logger.warning(msg)
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index fe687f342d00b95c0a44c1045a3e9718f8c1df47..63b31e76b5ea23947a59fff034c4367f50e1f83d 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -38,7 +38,6 @@
     "workflow": {
         "activate_iptrunk": "Activate IP Trunk",
         "activate_router": "Activate router",
-        "cancel_subscription": "Cancel subscription",
         "confirm_info": "Please verify this form looks correct.",
         "deploy_twamp": "Deploy TWAMP",
         "migrate_iptrunk": "Migrate IP Trunk",
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index 6c30324ed0b81064bdc4c84e862f1a0ff671b9da..0241e836ef8c9db3bcc750b357e03c65d291914d 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -25,7 +25,7 @@ class LAGMember(BaseModel):
     """A :term:`LAG` member interface that consists of a name and description."""
 
     interface_name: str
-    interface_description: str
+    interface_description: str | None
 
     def __hash__(self) -> int:
         """Calculate the hash based on the interface name and description, so that uniqueness can be determined."""
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index 05848a322c6b893231a8c754954c43ecf38da943..1e89ec894bf9b3029484db458b6ea19e592c6c55 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -1,17 +1,26 @@
 """Initialisation class that imports all workflows into :term:`GSO`."""
 
 from orchestrator.services.subscriptions import WF_USABLE_MAP
+from orchestrator.types import SubscriptionLifecycle
 from orchestrator.workflows import LazyWorkflowInstance
 
+ALL_ALIVE_STATES: list[str] = [
+    SubscriptionLifecycle.INITIAL,
+    SubscriptionLifecycle.PROVISIONING,
+    SubscriptionLifecycle.ACTIVE,
+]
+
 WF_USABLE_MAP.update(
     {
-        "cancel_subscription": ["initial"],
-        "redeploy_base_config": ["provisioning", "active"],
-        "update_ibgp_mesh": ["provisioning", "active"],
-        "activate_router": ["provisioning"],
-        "deploy_twamp": ["provisioning", "active"],
-        "modify_trunk_interface": ["provisioning", "active"],
-        "activate_iptrunk": ["provisioning"],
+        "redeploy_base_config": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
+        "update_ibgp_mesh": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
+        "activate_router": [SubscriptionLifecycle.PROVISIONING],
+        "deploy_twamp": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
+        "modify_trunk_interface": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
+        "activate_iptrunk": [SubscriptionLifecycle.PROVISIONING],
+        "terminate_site": ALL_ALIVE_STATES,
+        "terminate_router": ALL_ALIVE_STATES,
+        "terminate_iptrunk": ALL_ALIVE_STATES,
     }
 )
 
@@ -28,7 +37,6 @@ LazyWorkflowInstance("gso.workflows.router.redeploy_base_config", "redeploy_base
 LazyWorkflowInstance("gso.workflows.router.terminate_router", "terminate_router")
 LazyWorkflowInstance("gso.workflows.router.update_ibgp_mesh", "update_ibgp_mesh")
 LazyWorkflowInstance("gso.workflows.router.modify_connection_strategy", "modify_connection_strategy")
-LazyWorkflowInstance("gso.workflows.shared.cancel_subscription", "cancel_subscription")
 LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
 LazyWorkflowInstance("gso.workflows.site.modify_site", "modify_site")
 LazyWorkflowInstance("gso.workflows.site.terminate_site", "terminate_site")
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index b4dbed48a5cb6e9ef389147b71ac985e750987fa..fed3ab25b20b27a205481ca9fc685528670284d3 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -57,11 +57,11 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
 
         tt_number: str
         partner: str = ReadOnlyField("GEANT")
-        geant_s_sid: str
+        geant_s_sid: str | None
         iptrunk_description: str
         iptrunk_type: IptrunkType
         iptrunk_speed: PhysicalPortCapacity
-        iptrunk_minimum_links: int
+        iptrunk_number_of_members: int
 
         @validator("tt_number", allow_reuse=True)
         def validate_tt_number(cls, tt_number: str) -> str:
@@ -69,6 +69,13 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
 
     initial_user_input = yield CreateIptrunkForm
 
+    class VerifyMinimumLinksForm(FormPage):
+        info_label: Label = (
+            f"This is the calculated minimum-links for this LAG: " f"{initial_user_input.iptrunk_number_of_members - 1}"  # type: ignore[assignment]
+        )
+        info_label2: Label = "Please confirm or modify."  # type: ignore[assignment]
+
+    yield VerifyMinimumLinksForm
     router_enum_a = Choice("Select a router", zip(routers.keys(), routers.items(), strict=True))  # type: ignore[arg-type]
 
     class SelectRouterSideA(FormPage):
@@ -86,7 +93,8 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
     router_a_fqdn = Router.from_subscription(router_a).router.router_fqdn
 
     class JuniperAeMembers(UniqueConstrainedList[LAGMember]):
-        min_items = initial_user_input.iptrunk_minimum_links
+        min_items = initial_user_input.iptrunk_number_of_members
+        max_items = initial_user_input.iptrunk_number_of_members
 
     if get_router_vendor(router_a) == Vendor.NOKIA:
 
@@ -97,7 +105,8 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
             )
 
         class NokiaAeMembersA(UniqueConstrainedList[NokiaLAGMemberA]):
-            min_items = initial_user_input.iptrunk_minimum_links
+            min_items = initial_user_input.iptrunk_number_of_members
+            max_items = initial_user_input.iptrunk_number_of_members
 
         ae_members_side_a = NokiaAeMembersA
     else:
@@ -108,7 +117,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
             title = f"Provide subscription details for side A of the trunk.({router_a_fqdn})"
 
         side_a_ae_iface: available_lags_choices(router_a) or str  # type: ignore[valid-type]
-        side_a_ae_geant_a_sid: str
+        side_a_ae_geant_a_sid: str | None
         side_a_ae_members: ae_members_side_a  # type: ignore[valid-type]
 
         @validator("side_a_ae_members", allow_reuse=True)
@@ -159,7 +168,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
             title = f"Provide subscription details for side B of the trunk.({router_b_fqdn})"
 
         side_b_ae_iface: available_lags_choices(router_b) or str  # type: ignore[valid-type]
-        side_b_ae_geant_a_sid: str
+        side_b_ae_geant_a_sid: str | None
         side_b_ae_members: ae_members_side_b  # type: ignore[valid-type]
 
         @validator("side_b_ae_members", allow_reuse=True)
@@ -209,18 +218,18 @@ def get_info_from_ipam(subscription: IptrunkInactive) -> State:
 @step("Initialize subscription")
 def initialize_subscription(
     subscription: IptrunkInactive,
-    geant_s_sid: str,
+    geant_s_sid: str | None,
     iptrunk_type: IptrunkType,
     iptrunk_description: str,
     iptrunk_speed: PhysicalPortCapacity,
-    iptrunk_minimum_links: int,
+    iptrunk_number_of_members: int,
     side_a_node_id: str,
     side_a_ae_iface: str,
-    side_a_ae_geant_a_sid: str,
+    side_a_ae_geant_a_sid: str | None,
     side_a_ae_members: list[dict],
     side_b_node_id: str,
     side_b_ae_iface: str,
-    side_b_ae_geant_a_sid: str,
+    side_b_ae_geant_a_sid: str | None,
     side_b_ae_members: list[dict],
 ) -> State:
     """Take all input from the user, and store it in the database."""
@@ -232,7 +241,7 @@ def initialize_subscription(
     subscription.iptrunk.iptrunk_type = iptrunk_type
     subscription.iptrunk.iptrunk_speed = iptrunk_speed
     subscription.iptrunk.iptrunk_isis_metric = oss_params.GENERAL.isis_high_metric
-    subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
+    subscription.iptrunk.iptrunk_minimum_links = iptrunk_number_of_members - 1
 
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = side_a
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface
diff --git a/gso/workflows/iptrunk/deploy_twamp.py b/gso/workflows/iptrunk/deploy_twamp.py
index b9003078a016313bab3012cfee8eaf3f11f278ab..64483e60b8cae8717ad553c3f03bb03651fa6299 100644
--- a/gso/workflows/iptrunk/deploy_twamp.py
+++ b/gso/workflows/iptrunk/deploy_twamp.py
@@ -1,9 +1,12 @@
 """Workflow for adding TWAMP to an existing IP trunk."""
 
+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.utils.json import json_dumps
 from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
@@ -38,9 +41,10 @@ def _initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
     """Perform a dry run of deploying the TWAMP session."""
     extra_vars = {
-        "subscription": subscription,
+        "subscription": json.loads(json_dumps(subscription)),
         "process_id": process_id,
         "dry_run": True,
+        "verb": "deploy",
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP",
     }
 
@@ -58,9 +62,10 @@ def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route:
 def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State:
     """Deploy the TWAMP session."""
     extra_vars = {
-        "subscription": subscription,
+        "subscription": json.loads(json_dumps(subscription)),
         "process_id": process_id,
         "dry_run": False,
+        "verb": "deploy",
         "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP",
     }
 
@@ -74,6 +79,24 @@ def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route
     return {"subscription": subscription}
 
 
+@step("Check TWAMP status on both sides")
+def check_twamp_status(subscription: Iptrunk, callback_route: str) -> State:
+    """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}
+
+
 @workflow(
     "Deploy TWAMP",
     initial_input_form=wrap_modify_initial_input_form(_initial_input_form_generator),
@@ -90,6 +113,7 @@ def deploy_twamp() -> StepList:
         >> unsync
         >> lso_interaction(deploy_twamp_dry)
         >> lso_interaction(deploy_twamp_real)
+        >> lso_interaction(check_twamp_status)
         >> resync
         >> done
     )
diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index 540505d061f834b13bfae8855b592f0c140decc7..96ee4abb3ea30356ae701c90c22f2db27669c949 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -15,7 +15,6 @@ from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Choice, Label, UniqueConstrainedList
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, UUIDstr
-from orchestrator.utils.errors import ProcessFailureError
 from orchestrator.utils.json import json_dumps
 from orchestrator.workflow import StepList, conditional, done, init, inputstep
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
@@ -28,7 +27,6 @@ from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
 from gso.services import infoblox
-from gso.services.infoblox import DeletionError
 from gso.services.lso_client import execute_playbook, lso_interaction
 from gso.services.netbox_client import NetboxClient
 from gso.services.subscriptions import get_active_router_subscriptions
@@ -609,12 +607,7 @@ def update_ipam(subscription: Iptrunk, replace_index: int, new_node: Router, new
     v6_addr = subscription.iptrunk.iptrunk_ipv6_network[replace_index + 1]
 
     #  Out with the old
-    try:
-        infoblox.delete_host_by_ip(subscription.iptrunk.iptrunk_ipv4_network[replace_index])
-    except DeletionError as e:
-        msg = "Failed to delete record from Infoblox."
-        raise ProcessFailureError(msg) from e
-
+    infoblox.delete_host_by_ip(subscription.iptrunk.iptrunk_ipv4_network[replace_index])
     #  And in with the new
     new_fqdn = f"{new_lag_interface}-0.{new_node.router.router_fqdn}"
     comment = str(subscription.subscription_id)
diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py
index d3b5e60ee539660e7aa2fbd22088d20c8235e6cb..27111c62cbba9a1a92c173a035a3df163ac29d3f 100644
--- a/gso/workflows/iptrunk/modify_trunk_interface.py
+++ b/gso/workflows/iptrunk/modify_trunk_interface.py
@@ -40,7 +40,7 @@ def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_
     """Initialize the list of AE members."""
     router = subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_node
     router_vendor = get_router_vendor(router.owner_subscription_id)
-    iptrunk_minimum_link = initial_user_input["iptrunk_minimum_links"]
+    iptrunk_number_of_members = initial_user_input["iptrunk_number_of_members"]
     if router_vendor == Vendor.NOKIA:
         iptrunk_speed = initial_user_input["iptrunk_speed"]
 
@@ -61,13 +61,15 @@ def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_
             )
 
         class NokiaAeMembers(UniqueConstrainedList[NokiaLAGMember]):
-            min_items = iptrunk_minimum_link
+            min_items = iptrunk_number_of_members
+            max_items = iptrunk_number_of_members
 
         ae_members = NokiaAeMembers
     else:
 
         class JuniperAeMembers(UniqueConstrainedList[LAGMember]):
-            min_items = iptrunk_minimum_link
+            min_items = iptrunk_number_of_members
+            max_items = iptrunk_number_of_members
 
         ae_members = JuniperAeMembers  # type: ignore[assignment]
     return ae_members  # type: ignore[return-value]
@@ -79,7 +81,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
     class ModifyIptrunkForm(FormPage):
         tt_number: str
-        geant_s_sid: str = subscription.iptrunk.geant_s_sid
+        geant_s_sid: str | None = subscription.iptrunk.geant_s_sid
         iptrunk_description: str = subscription.iptrunk.iptrunk_description
         iptrunk_type: IptrunkType = subscription.iptrunk.iptrunk_type
         warning_label: Label = (
@@ -87,7 +89,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
             "You will need to add the new AE members in the next steps."  # type: ignore[assignment]
         )
         iptrunk_speed: PhysicalPortCapacity = subscription.iptrunk.iptrunk_speed
-        iptrunk_minimum_links: int = subscription.iptrunk.iptrunk_minimum_links
+        iptrunk_number_of_members: int = subscription.iptrunk.iptrunk_minimum_links + 1
         iptrunk_isis_metric: int = ReadOnlyField(subscription.iptrunk.iptrunk_isis_metric)
         iptrunk_ipv4_network: ipaddress.IPv4Network = ReadOnlyField(subscription.iptrunk.iptrunk_ipv4_network)
         iptrunk_ipv6_network: ipaddress.IPv6Network = ReadOnlyField(subscription.iptrunk.iptrunk_ipv6_network)
@@ -97,6 +99,14 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
             return validate_tt_number(tt_number)
 
     initial_user_input = yield ModifyIptrunkForm
+
+    class VerifyMinimumLinksForm(FormPage):
+        info_label: Label = (
+            f"This is the calculated minimum-links for this LAG: " f"{initial_user_input.iptrunk_number_of_members - 1}"  # type: ignore[assignment]
+        )
+        info_label2: Label = "Please confirm or modify."  # type: ignore[assignment]
+
+    yield VerifyMinimumLinksForm
     ae_members_side_a = initialize_ae_members(subscription, initial_user_input.dict(), 0)
 
     class ModifyIptrunkSideAForm(FormPage):
@@ -105,7 +115,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
         side_a_node: str = ReadOnlyField(subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn)
         side_a_ae_iface: str = ReadOnlyField(subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface)
-        side_a_ae_geant_a_sid: str = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid
+        side_a_ae_geant_a_sid: str | None = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid
         side_a_ae_members: ae_members_side_a = (  # type: ignore[valid-type]
             subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members
             if initial_user_input.iptrunk_speed == subscription.iptrunk.iptrunk_speed
@@ -130,7 +140,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
         side_b_node: str = ReadOnlyField(subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn)
         side_b_ae_iface: str = ReadOnlyField(subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_iface)
-        side_b_ae_geant_a_sid: str = subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_geant_a_sid
+        side_b_ae_geant_a_sid: str | None = subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_geant_a_sid
         side_b_ae_members: ae_members_side_b = (  # type: ignore[valid-type]
             subscription.iptrunk.iptrunk_sides[1].iptrunk_side_ae_members
             if initial_user_input.iptrunk_speed == subscription.iptrunk.iptrunk_speed
@@ -154,14 +164,14 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 @step("Update subscription")
 def modify_iptrunk_subscription(
     subscription: Iptrunk,
-    geant_s_sid: str,
+    geant_s_sid: str | None,
     iptrunk_type: IptrunkType,
     iptrunk_description: str,
     iptrunk_speed: PhysicalPortCapacity,
-    iptrunk_minimum_links: int,
-    side_a_ae_geant_a_sid: str,
+    iptrunk_number_of_members: int,
+    side_a_ae_geant_a_sid: str | None,
     side_a_ae_members: list[dict],
-    side_b_ae_geant_a_sid: str,
+    side_b_ae_geant_a_sid: str | None,
     side_b_ae_members: list[dict],
 ) -> State:
     """Modify the subscription in the service database, reflecting the changes to the newly selected interfaces."""
@@ -187,7 +197,7 @@ def modify_iptrunk_subscription(
     subscription.iptrunk.iptrunk_description = iptrunk_description
     subscription.iptrunk.iptrunk_type = iptrunk_type
     subscription.iptrunk.iptrunk_speed = iptrunk_speed
-    subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
+    subscription.iptrunk.iptrunk_minimum_links = iptrunk_number_of_members - 1
 
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid = side_a_ae_geant_a_sid
     #  Flush the old list of member interfaces
diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py
index 7a2afe6c22f0e2a1f381c92734f0efd5a8dbbb25..a2cb6727215a9f6184859c548dba7c3162127559 100644
--- a/gso/workflows/iptrunk/terminate_iptrunk.py
+++ b/gso/workflows/iptrunk/terminate_iptrunk.py
@@ -28,18 +28,23 @@ from gso.utils.shared_enums import Vendor
 from gso.utils.workflow_steps import set_isis_to_max
 
 
-def initial_input_form_generator() -> FormGenerator:
+def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     """Ask the operator to confirm whether router configuration and :term:`IPAM` resources should be deleted."""
+    iptrunk = Iptrunk.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
+        if iptrunk.status == SubscriptionLifecycle.INITIAL:
+            info_label_2: Label = (
+                "This will immediately mark the subscription as terminated, preventing any other workflows from "  # type:ignore[assignment]
+                "interacting with this product subscription."
+            )
+            info_label_3: Label = "ONLY EXECUTE THIS WORKFLOW WHEN YOU ARE ABSOLUTELY SURE WHAT YOU ARE DOING."  # type:ignore[assignment]
+
+        tt_number: str
         termination_label: Label = (
-            "Please confirm whether configuration should get removed from the A and B sides of the trunk, and whether "
-            "IPAM and Netbox resources should be released."  # type: ignore[assignment]
+            "Please confirm whether configuration should get removed from the A and B sides of the trunk."  # type: ignore[assignment]
         )
-        tt_number: str
         remove_configuration: bool = True
-        clean_up_ipam: bool = True
-        clean_up_netbox: bool = True
 
         @validator("tt_number", allow_reuse=True)
         def validate_tt_number(cls, tt_number: str) -> str:
@@ -148,22 +153,19 @@ def terminate_iptrunk() -> StepList:
     * Let the operator decide whether to remove configuration from the routers, if so:
         * Set the :term:`ISIS` metric of the IP trunk to an arbitrarily high value
         * Disable and remove configuration from the routers, first as a dry run
-    * Mark the IP trunk interfaces as free in Netbox, if selected by the operator
-    * Clear :term:`IPAM` resources, if selected by the operator
+    * Mark the IP trunk interfaces as free in Netbox
+    * Clear :term:`IPAM` resources
     * Terminate the subscription in the service database
     """
     run_config_steps = conditional(lambda state: state["remove_configuration"])
-    run_ipam_steps = conditional(lambda state: state["clean_up_ipam"])
     side_a_is_nokia = conditional(
-        lambda state: state["clean_up_netbox"]
-        and get_router_vendor(
+        lambda state: get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["owner_subscription_id"]
         )
         == Vendor.NOKIA
     )
     side_b_is_nokia = conditional(
-        lambda state: state["clean_up_netbox"]
-        and get_router_vendor(
+        lambda state: get_router_vendor(
             state["subscription"]["iptrunk"]["iptrunk_sides"][1]["iptrunk_side_node"]["owner_subscription_id"]
         )
         == Vendor.NOKIA
@@ -175,7 +177,6 @@ def terminate_iptrunk() -> StepList:
         >> lso_interaction(deprovision_ip_trunk_dry)
         >> lso_interaction(deprovision_ip_trunk_real)
     )
-    ipam_steps = init >> deprovision_ip_trunk_ipv4 >> deprovision_ip_trunk_ipv6
 
     return (
         init
@@ -184,7 +185,8 @@ def terminate_iptrunk() -> StepList:
         >> run_config_steps(config_steps)
         >> side_a_is_nokia(netbox_clean_up_side_a)
         >> side_b_is_nokia(netbox_clean_up_side_b)
-        >> run_ipam_steps(ipam_steps)
+        >> deprovision_ip_trunk_ipv4
+        >> deprovision_ip_trunk_ipv6
         >> set_status(SubscriptionLifecycle.TERMINATED)
         >> resync
         >> done
diff --git a/gso/workflows/router/terminate_router.py b/gso/workflows/router/terminate_router.py
index 8abc8877367f5c9333852de0e19720f6f01d25d8..0d46f9abbd7a32e2f141894a09751659d24d52fa 100644
--- a/gso/workflows/router/terminate_router.py
+++ b/gso/workflows/router/terminate_router.py
@@ -32,13 +32,16 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     router = Router.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
-        termination_label: Label = (
-            "Please confirm whether configuration should get removed from the router, and whether IPAM resources should"
-            " be released."  # type: ignore[assignment]
-        )
+        if router.status == SubscriptionLifecycle.INITIAL:
+            info_label_2: Label = (
+                "This will immediately mark the subscription as terminated, preventing any other workflows from "  # type:ignore[assignment]
+                "interacting with this product subscription."
+            )
+            info_label_3: Label = "ONLY EXECUTE THIS WORKFLOW WHEN YOU ARE ABSOLUTELY SURE WHAT YOU ARE DOING."  # type:ignore[assignment]
+
         tt_number: str
+        termination_label: Label = "Please confirm whether configuration should get removed from the router."  # type: ignore[assignment]
         remove_configuration: bool = True
-        clean_up_ipam: bool = True
 
     user_input = yield TerminateForm
     return user_input.dict() | {"router_is_nokia": router.router.vendor == Vendor.NOKIA}
@@ -114,7 +117,6 @@ def terminate_router() -> StepList:
     * Disable and delete configuration on the router, if selected by the operator
     * Mark the subscription as terminated in the service database
     """
-    run_ipam_steps = conditional(lambda state: state["clean_up_ipam"])
     run_config_steps = conditional(lambda state: state["remove_configuration"])
     router_is_nokia = conditional(lambda state: state["router_is_nokia"])
 
@@ -122,7 +124,7 @@ def terminate_router() -> StepList:
         init
         >> store_process_subscription(Target.TERMINATE)
         >> unsync
-        >> run_ipam_steps(deprovision_loopback_ips)
+        >> deprovision_loopback_ips
         >> run_config_steps(lso_interaction(remove_config_from_router_dry))
         >> run_config_steps(lso_interaction(remove_config_from_router_real))
         >> router_is_nokia(remove_device_from_netbox)
diff --git a/gso/workflows/shared/__init__.py b/gso/workflows/shared/__init__.py
deleted file mode 100644
index daa25805d2c02ae05d7292e4a74a649d93a07e6b..0000000000000000000000000000000000000000
--- a/gso/workflows/shared/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Workflows that are shared across multiple products."""
diff --git a/gso/workflows/shared/cancel_subscription.py b/gso/workflows/shared/cancel_subscription.py
deleted file mode 100644
index 79bb05905f6940ce3eda6543d960acf41818fb4f..0000000000000000000000000000000000000000
--- a/gso/workflows/shared/cancel_subscription.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Cancel a subscription that is in the initial lifecycle state."""
-
-from orchestrator.forms import FormPage
-from orchestrator.forms.validators import Label
-from orchestrator.targets import Target
-from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
-from orchestrator.workflow import StepList, done, init, workflow
-from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
-from orchestrator.workflows.utils import wrap_modify_initial_input_form
-
-
-def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator:
-    class CancelSubscriptionForm(FormPage):
-        info_label: Label = f"Canceling subscription with ID {subscription_id}"  # type:ignore[assignment]
-        info_label_2: Label = (
-            "This will immediately mark the subscription as terminated, preventing any other workflows from interacting"  # type:ignore[assignment]
-            " with this product subscription."
-        )
-        info_label_3: Label = "ONLY EXECUTE THIS WORKFLOW WHEN YOU ARE ABSOLUTELY SURE WHAT YOU ARE DOING."  # type:ignore[assignment]
-        info_label_4: Label = "THIS WORKFLOW IS IRREVERSIBLE AND MAY HAVE UNFORESEEN CONSEQUENCES."  # type:ignore[assignment]
-
-    yield CancelSubscriptionForm
-
-    return {"subscription_id": subscription_id}
-
-
-@workflow(
-    "Cancel an initial subscription",
-    initial_input_form=wrap_modify_initial_input_form(_initial_input_form),
-    target=Target.TERMINATE,
-)
-def cancel_subscription() -> StepList:
-    """Cancel an initial subscription, taking it from the ``INITIAL`` state straight to ``TERMINATED``.
-
-    This workflow can be used when a creation workflow has failed, and the process needs to be restarted. This workflow
-    will prevent a stray subscription, forever stuck in the initial state, to stick around.
-
-    * Update the subscription lifecycle state to ``TERMINATED``.
-    """
-    return (
-        init
-        >> store_process_subscription(Target.TERMINATE)
-        >> unsync
-        >> set_status(SubscriptionLifecycle.TERMINATED)
-        >> resync
-        >> done
-    )
diff --git a/gso/workflows/site/terminate_site.py b/gso/workflows/site/terminate_site.py
index 91be194f181f13422cf349d7ca0f65826b3a6a2a..96e807b47ed9c72b101ad3c6303e9b1fbd1405bc 100644
--- a/gso/workflows/site/terminate_site.py
+++ b/gso/workflows/site/terminate_site.py
@@ -18,9 +18,16 @@ from gso.products.product_types.site import Site
 
 def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
     """Ask the user for confirmation whether to terminate the selected site."""
-    Site.from_subscription(subscription_id)
+    site = Site.from_subscription(subscription_id)
 
     class TerminateForm(FormPage):
+        if site.status == SubscriptionLifecycle.INITIAL:
+            info_label_2: Label = (
+                "This will immediately mark the subscription as terminated, preventing any other workflows from "  # type:ignore[assignment]
+                "interacting with this product subscription."
+            )
+            info_label_3: Label = "ONLY EXECUTE THIS WORKFLOW WHEN YOU ARE ABSOLUTELY SURE WHAT YOU ARE DOING."  # type:ignore[assignment]
+
         termination_label: Label = "Are you sure you want to delete this site?"  # type: ignore[assignment]
 
     user_input = yield TerminateForm
diff --git a/gso/workflows/tasks/import_iptrunk.py b/gso/workflows/tasks/import_iptrunk.py
index 648d954f94ae57f9471826bba04402684508de81..9c6687b35943f7316a4cf4758009598270f80b77 100644
--- a/gso/workflows/tasks/import_iptrunk.py
+++ b/gso/workflows/tasks/import_iptrunk.py
@@ -39,7 +39,7 @@ def initial_input_form_generator() -> FormGenerator:
             title = "Import Iptrunk"
 
         partner: str
-        geant_s_sid: str
+        geant_s_sid: str | None
         iptrunk_description: str
         iptrunk_type: IptrunkType
         iptrunk_speed: PhysicalPortCapacity
@@ -48,12 +48,12 @@ def initial_input_form_generator() -> FormGenerator:
 
         side_a_node_id: router_enum  # type: ignore[valid-type]
         side_a_ae_iface: str
-        side_a_ae_geant_a_sid: str
+        side_a_ae_geant_a_sid: str | None
         side_a_ae_members: UniqueConstrainedList[LAGMember]
 
         side_b_node_id: router_enum  # type: ignore[valid-type]
         side_b_ae_iface: str
-        side_b_ae_geant_a_sid: str
+        side_b_ae_geant_a_sid: str | None
         side_b_ae_members: UniqueConstrainedList[LAGMember]
 
         iptrunk_ipv4_network: ipaddress.IPv4Network
@@ -80,7 +80,7 @@ def create_subscription(partner: str) -> State:
 @step("Initialize subscription")
 def initialize_subscription(
     subscription: IptrunkInactive,
-    geant_s_sid: str,
+    geant_s_sid: str | None,
     iptrunk_type: IptrunkType,
     iptrunk_description: str,
     iptrunk_speed: PhysicalPortCapacity,
@@ -88,11 +88,11 @@ def initialize_subscription(
     iptrunk_isis_metric: int,
     side_a_node_id: str,
     side_a_ae_iface: str,
-    side_a_ae_geant_a_sid: str,
+    side_a_ae_geant_a_sid: str | None,
     side_a_ae_members: list[dict],
     side_b_node_id: str,
     side_b_ae_iface: str,
-    side_b_ae_geant_a_sid: str,
+    side_b_ae_geant_a_sid: str | None,
     side_b_ae_members: list[dict],
 ) -> State:
     """Take all input from the user, and store it in the database."""
diff --git a/setup.py b/setup.py
index 82661968f448d6d87fdf7583360a6be254d6f41c..2fcf9799777b7fc3a7bbd56c99307d3818fbfe7e 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
 
 setup(
     name="geant-service-orchestrator",
-    version="1.4",
+    version="1.5",
     author="GÉANT Orchestration and Automation Team",
     author_email="goat@geant.org",
     description="GÉANT Service Orchestrator",
diff --git a/test/services/test_infoblox.py b/test/services/test_infoblox.py
index bebcdae602f852f4fb92fab4bfbeab85e7f12d26..3a7332606ed6314c104fd281a1c8c03bfcd18a91 100644
--- a/test/services/test_infoblox.py
+++ b/test/services/test_infoblox.py
@@ -1,4 +1,5 @@
 import ipaddress
+import logging
 import re
 from os import PathLike
 
@@ -7,7 +8,7 @@ import responses
 from requests import codes
 
 from gso.services import infoblox
-from gso.services.infoblox import AllocationError, DeletionError
+from gso.services.infoblox import AllocationError
 
 
 def _set_up_network_responses():
@@ -216,17 +217,16 @@ def test_delete_good_network(data_config_filename: PathLike):
 
 
 @responses.activate
-def test_delete_non_existent_network(data_config_filename: PathLike):
+def test_delete_non_existent_network(data_config_filename: PathLike, caplog):
     responses.add(
         method=responses.GET,
         url="https://10.0.0.1/wapi/v2.12/network?network=10.255.255.0%2F26&_return_fields=comment%2Cextattrs%2Cnetwork%"
         "2Cnetwork_view",
         json=[],
     )
-
-    with pytest.raises(DeletionError) as e:
-        infoblox.delete_network(ipaddress.IPv4Network("10.255.255.0/26"))
-    assert e.value.args[0] == "Could not find network 10.255.255.0/26, nothing has been deleted."
+    caplog.set_level(logging.WARNING)
+    infoblox.delete_network(ipaddress.IPv4Network("10.255.255.0/26"))
+    assert "Could not find network 10.255.255.0/26, nothing has been deleted." in caplog.text
 
 
 @responses.activate
@@ -277,21 +277,19 @@ def test_delete_good_host(data_config_filename: PathLike):
 
 
 @responses.activate
-def test_delete_bad_host(data_config_filename: PathLike):
+def test_delete_bad_host(data_config_filename: PathLike, caplog):
     responses.add(
         method=responses.GET,
         url=re.compile(r".+"),
         json=[],
     )
+    caplog.set_level(logging.WARNING)
 
-    with pytest.raises(DeletionError) as e:
-        infoblox.delete_host_by_ip(ipaddress.IPv4Address("10.255.255.1"))
-    assert e.value.args[0] == "Could not find host at 10.255.255.1, nothing has been deleted."
+    infoblox.delete_host_by_ip(ipaddress.IPv4Address("10.255.255.1"))
+    assert "Could not find host at 10.255.255.1, nothing has been deleted." in caplog.text
 
-    with pytest.raises(DeletionError) as e:
-        infoblox.delete_host_by_ip(ipaddress.IPv6Address("dead:beef::1"))
-    assert e.value.args[0] == "Could not find host at dead:beef::1, nothing has been deleted."
+    infoblox.delete_host_by_ip(ipaddress.IPv6Address("dead:beef::1"))
+    assert "Could not find host at dead:beef::1, nothing has been deleted." in caplog.text
 
-    with pytest.raises(DeletionError) as e:
-        infoblox.delete_host_by_fqdn("fake.host.net")
-    assert e.value.args[0] == "Could not find host at fake.host.net, nothing has been deleted."
+    infoblox.delete_host_by_fqdn("fake.host.net")
+    assert "Could not find host at fake.host.net, nothing has been deleted." in caplog.text
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index 34a79604ef532c41cf141214c88b5790b810aeef..4163f6835166d368cbda5a007d24f7043bd8e7f4 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -52,12 +52,12 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r
     # Set side b router to Juniper
     if vendor == Vendor.JUNIPER:
         router_side_b = juniper_router_subscription_factory()
-        side_b_members = faker.link_members_juniper()
+        side_b_members = faker.link_members_juniper()[0:2]
     else:
         router_side_b = nokia_router_subscription_factory()
         side_b_members = [
             LAGMember(interface_name=f"Interface{interface}", interface_description=faker.sentence())
-            for interface in range(5)
+            for interface in range(2)
         ]
 
     create_ip_trunk_step = {
@@ -66,18 +66,19 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r
         "iptrunk_type": IptrunkType.DARK_FIBER,
         "iptrunk_description": faker.sentence(),
         "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND,
-        "iptrunk_minimum_links": 2,
+        "iptrunk_number_of_members": 2,
     }
+    create_ip_trunk_confirm_step = {}
     create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a}
     create_ip_trunk_side_a_step = {
         "side_a_ae_iface": "lag-1",
-        "side_a_ae_geant_a_sid": faker.geant_sid(),
+        "side_a_ae_geant_a_sid": None,
         "side_a_ae_members": [
             LAGMember(
                 interface_name=f"Interface{interface}",
                 interface_description=faker.sentence(),
             )
-            for interface in range(5)
+            for interface in range(2)
         ],
     }
     create_ip_trunk_side_b_router_name = {"side_b_node_id": router_side_b}
@@ -89,6 +90,7 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r
 
     return [
         create_ip_trunk_step,
+        create_ip_trunk_confirm_step,
         create_ip_trunk_side_a_router_name,
         create_ip_trunk_side_a_step,
         create_ip_trunk_side_b_router_name,
diff --git a/test/workflows/iptrunk/test_deploy_twamp.py b/test/workflows/iptrunk/test_deploy_twamp.py
index c5592738f530a1769c5fd8dd56e02e426950c6ad..8584cd99cae310bccf70ac22a6cd377b5672cefa 100644
--- a/test/workflows/iptrunk/test_deploy_twamp.py
+++ b/test/workflows/iptrunk/test_deploy_twamp.py
@@ -25,7 +25,7 @@ def test_iptrunk_deploy_twamp_success(
     initial_input_data = [{"subscription_id": product_id}, {"tt_number": faker.tt_number()}]
     result, process_stat, step_log = run_workflow("deploy_twamp", initial_input_data)
 
-    for _ in range(2):
+    for _ in range(3):
         result, step_log = assert_lso_interaction_success(result, process_stat, step_log)
 
     assert_complete(result)
@@ -35,4 +35,4 @@ def test_iptrunk_deploy_twamp_success(
     subscription = Iptrunk.from_subscription(subscription_id)
 
     assert subscription.status == "active"
-    assert mock_execute_playbook.call_count == 2
+    assert mock_execute_playbook.call_count == 3
diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py
index 4dca28e963ef862f182749a9ce7c1b25c418c4b7..18524cfd70ba8a61a12db319ff4cd1af921c64fe 100644
--- a/test/workflows/iptrunk/test_modify_trunk_interface.py
+++ b/test/workflows/iptrunk/test_modify_trunk_interface.py
@@ -29,26 +29,26 @@ def input_form_iptrunk_data(
         side_node = juniper_router_subscription_factory()
         side_a_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node)
         side_b_node = iptrunk_side_subscription_factory()
-        new_side_a_ae_members = faker.link_members_juniper()
-        new_side_b_ae_members = faker.link_members_nokia()
+        new_side_a_ae_members = faker.link_members_juniper()[0:2]
+        new_side_b_ae_members = faker.link_members_nokia()[0:2]
     elif use_juniper == UseJuniperSide.SIDE_B:
         side_node = juniper_router_subscription_factory()
         side_a_node = iptrunk_side_subscription_factory()
         side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node)
-        new_side_a_ae_members = faker.link_members_nokia()
-        new_side_b_ae_members = faker.link_members_juniper()
+        new_side_a_ae_members = faker.link_members_nokia()[0:2]
+        new_side_b_ae_members = faker.link_members_juniper()[0:2]
     elif use_juniper == UseJuniperSide.SIDE_BOTH:
         side_node_1 = juniper_router_subscription_factory()
         side_node_2 = juniper_router_subscription_factory()
         side_a_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node_1)
         side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node_2)
-        new_side_a_ae_members = faker.link_members_juniper()
-        new_side_b_ae_members = faker.link_members_juniper()
+        new_side_a_ae_members = faker.link_members_juniper()[0:2]
+        new_side_b_ae_members = faker.link_members_juniper()[0:2]
     else:
         side_a_node = iptrunk_side_subscription_factory()
         side_b_node = iptrunk_side_subscription_factory()
-        new_side_a_ae_members = faker.link_members_nokia()
-        new_side_b_ae_members = faker.link_members_nokia()
+        new_side_a_ae_members = faker.link_members_nokia()[0:2]
+        new_side_b_ae_members = faker.link_members_nokia()[0:2]
 
     product_id = iptrunk_subscription_factory(iptrunk_sides=[side_a_node, side_b_node])
 
@@ -70,8 +70,9 @@ def input_form_iptrunk_data(
             "iptrunk_description": new_description,
             "iptrunk_type": new_type,
             "iptrunk_speed": new_speed,
-            "iptrunk_minimum_links": new_link_count,
+            "iptrunk_number_of_members": new_link_count,
         },
+        {},
         {
             "side_a_ae_geant_a_sid": new_side_a_sid,
             "side_a_ae_members": new_side_a_ae_members,
@@ -133,10 +134,10 @@ def test_iptrunk_modify_trunk_interface_success(
     assert mock_provision_ip_trunk.call_count == 2
     # Assert all Netbox calls have been made
     new_sid = input_form_iptrunk_data[1]["geant_s_sid"]
-    new_side_a_sid = input_form_iptrunk_data[2]["side_a_ae_geant_a_sid"]
-    new_side_a_ae_members = input_form_iptrunk_data[2]["side_a_ae_members"]
-    new_side_b_sid = input_form_iptrunk_data[3]["side_b_ae_geant_a_sid"]
-    new_side_b_ae_members = input_form_iptrunk_data[3]["side_b_ae_members"]
+    new_side_a_sid = input_form_iptrunk_data[3]["side_a_ae_geant_a_sid"]
+    new_side_a_ae_members = input_form_iptrunk_data[3]["side_a_ae_members"]
+    new_side_b_sid = input_form_iptrunk_data[4]["side_b_ae_geant_a_sid"]
+    new_side_b_ae_members = input_form_iptrunk_data[4]["side_b_ae_members"]
 
     # Only Nokia interfaces are checked
     vendor_side_a = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.vendor
@@ -162,7 +163,7 @@ def test_iptrunk_modify_trunk_interface_success(
     assert subscription.iptrunk.iptrunk_description == input_form_iptrunk_data[1]["iptrunk_description"]
     assert subscription.iptrunk.iptrunk_type == input_form_iptrunk_data[1]["iptrunk_type"]
     assert subscription.iptrunk.iptrunk_speed == input_form_iptrunk_data[1]["iptrunk_speed"]
-    assert subscription.iptrunk.iptrunk_minimum_links == input_form_iptrunk_data[1]["iptrunk_minimum_links"]
+    assert subscription.iptrunk.iptrunk_minimum_links == input_form_iptrunk_data[1]["iptrunk_number_of_members"] - 1
     assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid == new_side_a_sid
 
     def _find_interface_by_name(interfaces: list[dict[str, str]], name: str):
diff --git a/test/workflows/iptrunk/test_terminate_iptrunk.py b/test/workflows/iptrunk/test_terminate_iptrunk.py
index 05a94456ab00aa1145f2e1dae1953eae437568ee..c026e84bccc292fe236e1855e3f86a49d47a8ab1 100644
--- a/test/workflows/iptrunk/test_terminate_iptrunk.py
+++ b/test/workflows/iptrunk/test_terminate_iptrunk.py
@@ -42,7 +42,6 @@ def test_successful_iptrunk_termination(
         {
             "tt_number": faker.tt_number(),
             "remove_configuration": True,
-            "clean_up_ipam": True,
         },
     ]
     result, process_stat, step_log = run_workflow("terminate_iptrunk", initial_iptrunk_data)
diff --git a/test/workflows/router/test_terminate_router.py b/test/workflows/router/test_terminate_router.py
index bc978824b1f03f03fa7b06a8f5325f1d26959a27..e52826820084981798541f1189a134eda5d1d9c0 100644
--- a/test/workflows/router/test_terminate_router.py
+++ b/test/workflows/router/test_terminate_router.py
@@ -7,15 +7,7 @@ from test.workflows import assert_complete, assert_lso_interaction_success, extr
 
 
 @pytest.mark.workflow()
-@pytest.mark.parametrize(
-    ("remove_configuration", "clean_up_ipam"),
-    [
-        (True, True),
-        (True, False),
-        (False, True),
-        (False, False),
-    ],
-)
+@pytest.mark.parametrize("remove_configuration", [True, False])
 @patch("gso.services.lso_client._send_request")
 @patch("gso.workflows.router.terminate_router.NetboxClient.delete_device")
 @patch("gso.workflows.router.terminate_router.infoblox.delete_host_by_ip")
@@ -24,7 +16,6 @@ def test_terminate_router_full_success(
     mock_delete_device,
     mock_execute_playbook,
     remove_configuration,
-    clean_up_ipam,
     nokia_router_subscription_factory,
     faker,
     data_config_filename,
@@ -34,7 +25,6 @@ def test_terminate_router_full_success(
     router_termination_input_form_data = {
         "tt_number": faker.tt_number(),
         "remove_configuration": remove_configuration,
-        "clean_up_ipam": clean_up_ipam,
     }
     lso_interaction_count = 2 if remove_configuration else 0
 
@@ -53,5 +43,5 @@ def test_terminate_router_full_success(
 
     assert subscription.status == "terminated"
     assert mock_delete_device.call_count == 1
-    assert mock_delete_host_by_ip.call_count == (1 if clean_up_ipam else 0)
+    assert mock_delete_host_by_ip.call_count == 1
     assert mock_execute_playbook.call_count == lso_interaction_count
diff --git a/test/workflows/shared/__init__.py b/test/workflows/shared/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/test/workflows/shared/cancel_subscription.py b/test/workflows/shared/cancel_subscription.py
deleted file mode 100644
index 9963b8f21066cfd02408a819b8ce2ae5facf8cba..0000000000000000000000000000000000000000
--- a/test/workflows/shared/cancel_subscription.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import pytest
-from orchestrator.domain.base import SubscriptionModel
-from orchestrator.types import SubscriptionLifecycle
-
-from test.workflows import assert_complete, extract_state, run_workflow
-
-
-@pytest.mark.parametrize(
-    "subscription_factory",
-    [
-        "site_subscription_factory",
-        "juniper_router_subscription_factory",
-        "nokia_router_subscription_factory",
-        "iptrunk_subscription_factory",
-    ],
-)
-@pytest.mark.workflow()
-def test_cancel_workflow_success(subscription_factory, geant_partner, request):
-    subscription_id = request.getfixturevalue(subscription_factory)(
-        status=SubscriptionLifecycle.INITIAL,
-        partner=geant_partner,
-    )
-    initial_site_data = [{"subscription_id": subscription_id}, {}]
-    result, _, _ = run_workflow("cancel_subscription", initial_site_data)
-    assert_complete(result)
-
-    state = extract_state(result)
-    subscription_id = state["subscription_id"]
-    subscription = SubscriptionModel.from_subscription(subscription_id)
-    assert subscription.status == "terminated"