diff --git a/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py b/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d176c13f26ce5beb56c2d29b94c3226b767382b
--- /dev/null
+++ b/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py
@@ -0,0 +1,39 @@
+"""Add router validation task.
+
+Revision ID: 6c986f219e3f
+Revises: f0764c6f392c
+Create Date: 2023-09-22 17:13:01.223133
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '6c986f219e3f'
+down_revision = 'f0764c6f392c'
+branch_labels = None
+depends_on = None
+
+
+from orchestrator.migrations.helpers import create_workflow, delete_workflow
+
+new_workflows = [
+    {
+        "name": "validate_router",
+        "target": "SYSTEM",
+        "description": "Validate router configuration",
+        "product_type": "Router"
+    }
+]
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        create_workflow(conn, workflow)
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    for workflow in new_workflows:
+        delete_workflow(conn, workflow["name"])
diff --git a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py
index b2ae1da22f4836940059267a31ed4be661dfd300..e8e81febcde701a09ef26a92f4e2fe65b5799b71 100644
--- a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py
+++ b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py
@@ -1,7 +1,7 @@
 """Add IP Trunk workflows.
 
 Revision ID: 6dbd6c4c04b4
-Revises: 815033570ad7
+Revises: 0c31b60487c8
 Create Date: 2023-11-21 12:55:29.689402
 
 """
diff --git a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py
index 1d996616d410081c01938ade185fc45455e48046..ed03212a5e681390280bc455d3fe0ff9c7a19f33 100644
--- a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py
+++ b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py
@@ -1,7 +1,7 @@
 """Add router workflows.
 
 Revision ID: 815033570ad7
-Revises: 0c31b60487c8
+Revises: 0c904cf0b66b
 Create Date: 2023-11-21 12:53:26.413333
 
 """
diff --git a/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py b/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py
index 9e9782492fe7b6b1cc24a9a2eba2c92f0ec75be6..c0f3c473a67f0101a5bbf3e9b3991e9281de85b7 100644
--- a/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py
+++ b/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py
@@ -1,7 +1,7 @@
 """Add TWAMP deployment workflow.
 
 Revision ID: f0764c6f392c
-Revises: 815033570ad7
+Revises: b689d4636694
 Create Date: 2023-12-27 14:31:42.285180
 
 """
diff --git a/gso/services/netbox_client.py b/gso/services/netbox_client.py
index 212b1fb9d60ec1e26719a64b5074b74b4d03ae0f..570ecbeb1fb1865cc2c433fdf5047491deaa9a57 100644
--- a/gso/services/netbox_client.py
+++ b/gso/services/netbox_client.py
@@ -223,9 +223,7 @@ class NetboxClient:
         # Assign interface to :term:`LAG`, ensuring it does not already belong to a :term:`LAG`.
         if iface.lag:
             msg = f"The interface: {iface_name} on device: {device_name} already belongs to a LAG: {iface.lag.name}."
-            raise WorkflowStateError(
-                msg,
-            )
+            raise WorkflowStateError(msg)
         iface.lag = lag.id
 
         # Set description if provided
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index 9fdf1dfafa8ff3304e5e9cf6b0e36669f8412446..232bcd0a89b6816616de3db2eb03b25b5154e988 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -55,6 +55,7 @@
         "import_router": "NOT FOR HUMANS -- Finalize import into a Router product",
         "import_iptrunk": "NOT FOR HUMANS -- Finalize import into an IP trunk product",
         "import_office_router": "NOT FOR HUMANS -- Finalize import into an Office router product",
-        "import_super_pop_switch": "NOT FOR HUMANS -- Finalize import into a Super PoP switch"
+        "import_super_pop_switch": "NOT FOR HUMANS -- Finalize import into a Super PoP switch",
+        "validate_router": "Validate router configuration"
     }
 }
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index cf2eca0f71b724ca482b15107c7cf1844f554918..54d6a8a168442935f481e5f4d2fa6fde6f99919d 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -63,3 +63,4 @@ LazyWorkflowInstance("gso.workflows.office_router.create_imported_office_router"
 #  Opengear workflows
 LazyWorkflowInstance("gso.workflows.opengear.create_imported_opengear", "create_imported_opengear")
 LazyWorkflowInstance("gso.workflows.opengear.import_opengear", "import_opengear")
+
diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py
index 700f3f9c86112562d1a1ef127dad209da22b96be..dfa0b27b517c0233231fb11c14f335adc7749e44 100644
--- a/gso/workflows/router/create_router.py
+++ b/gso/workflows/router/create_router.py
@@ -7,6 +7,7 @@ from orchestrator.forms import FormPage
 from orchestrator.forms.validators import Choice, Label
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
+from orchestrator.utils.errors import ProcessFailureError
 from orchestrator.workflow import StepList, conditional, done, init, inputstep, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
@@ -143,13 +144,15 @@ def create_netbox_device(subscription: RouterInactive) -> State:
 
 
 @step("Verify IPAM resources for loopback interface")
-def verify_ipam_loopback(subscription: RouterInactive) -> State:
-    """Validate the :term:`IPAM` resources for the loopback interface."""
+def verify_ipam_loopback(subscription: RouterInactive) -> None:
+    """Validate the :term:`IPAM` resources for the loopback interface.
+
+    Raises an :class:`orchestrator.utils.errors.ProcessFailureError` if IPAM is configured incorrectly.
+    """
     host_record = infoblox.find_host_by_fqdn(f"lo0.{subscription.router.router_fqdn}")
     if not host_record or str(subscription.subscription_id) not in host_record.comment:
-        return {"ipam_warning": "Loopback record is incorrectly configured in IPAM, please investigate this manually!"}
-
-    return {"subscription": subscription}
+        msg = "Loopback record is incorrectly configured in IPAM, please investigate this manually!"
+        raise ProcessFailureError(msg)
 
 
 @inputstep("Prompt to reboot", assignee=Assignee.SYSTEM)
diff --git a/gso/workflows/tasks/validate_router.py b/gso/workflows/tasks/validate_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..18ab6e3e73564efed9bb90997ad5a3bdd13eeac3
--- /dev/null
+++ b/gso/workflows/tasks/validate_router.py
@@ -0,0 +1,41 @@
+import json
+
+from orchestrator.targets import Target
+from orchestrator.types import State
+from orchestrator.utils.json import json_dumps
+from orchestrator.workflow import StepList, init, workflow, done, step
+from orchestrator.workflows.steps import store_process_subscription, unsync, resync
+
+from gso.services.provisioning_proxy import pp_interaction, execute_playbook
+from gso.workflows.router.create_router import verify_ipam_loopback
+from products import Router
+
+
+@step("Validate router configuration")
+def validate_router_config(subscription: Router, callback_route: str) -> None:
+    extra_vars = {"wfo_router_json": json.loads(json_dumps(subscription)), "verb": "validate"}
+
+    execute_playbook(
+        playbook_name="base_config.yaml",
+        callback_route=callback_route,
+        inventory=subscription.router.router_fqdn,
+        extra_vars=extra_vars
+    )
+
+
+@workflow("Validate router configuration", target=Target.SYSTEM)
+def validate_router() -> StepList:
+    """Validate an existing, active Router subscription.
+
+    * Run an Ansible playbook to verify the configuration is intact.
+    * Verify that the loopback interface is correctly configured in IPAM.
+    """
+    return (
+        init
+        >> store_process_subscription(Target.SYSTEM)
+        >> unsync
+        >> pp_interaction(validate_router_config)
+        >> verify_ipam_loopback
+        >> resync
+        >> done
+    )