diff --git a/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py b/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7b03f9f4f1c2ccc9b953d8afa4b38819d0ea7e5
--- /dev/null
+++ b/gso/migrations/versions/2024-02-27_5bea5647f61d_add_ip_trunk_activation_workflow.py
@@ -0,0 +1,39 @@
+"""Add IP Trunk activation workflow.
+
+Revision ID: 5bea5647f61d
+Revises: 113a81d2a40a
+Create Date: 2024-02-27 17:01:57.300326
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '5bea5647f61d'
+down_revision = '113a81d2a40a'
+branch_labels = None
+depends_on = None
+
+
+from orchestrator.migrations.helpers import create_workflow, delete_workflow
+
+new_workflows = [
+    {
+        "name": "activate_iptrunk",
+        "target": "MODIFY",
+        "description": "Activate an IP Trunk",
+        "product_type": "Iptrunk"
+    }
+]
+
+
+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/workflows/__init__.py b/gso/workflows/__init__.py
index bbc40e87c7a13d3548eec7f730db0b03a2a92b91..3566d7fc4fcf997d286223edf0dc097fa2aa789e 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -8,9 +8,13 @@ WF_USABLE_MAP.update(
         "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"],
     }
 )
 
+LazyWorkflowInstance("gso.workflows.iptrunk.activate_iptrunk", "activate_iptrunk")
 LazyWorkflowInstance("gso.workflows.iptrunk.create_iptrunk", "create_iptrunk")
 LazyWorkflowInstance("gso.workflows.iptrunk.deploy_twamp", "deploy_twamp")
 LazyWorkflowInstance("gso.workflows.iptrunk.modify_isis_metric", "modify_isis_metric")
diff --git a/gso/workflows/iptrunk/activate_iptrunk.py b/gso/workflows/iptrunk/activate_iptrunk.py
new file mode 100644
index 0000000000000000000000000000000000000000..f686a8cb7e3c825dceffeb876c644a37342ce3d8
--- /dev/null
+++ b/gso/workflows/iptrunk/activate_iptrunk.py
@@ -0,0 +1,59 @@
+"""Activate IP trunk takes a provisioning trunk to the active lifecycle state."""
+
+from orchestrator.config.assignee import Assignee
+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, inputstep, workflow
+from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
+from orchestrator.workflows.utils import wrap_modify_initial_input_form
+
+from gso.products.product_types.iptrunk import Iptrunk
+
+
+def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator:
+    trunk = Iptrunk.from_subscription(subscription_id)
+
+    class ActivateTrunkForm(FormPage):
+        info_label: Label = "Start approval process for IP trunk activation."  # type:ignore[assignment]
+
+    user_input = yield ActivateTrunkForm
+
+    return user_input.dict() | {"subscription": trunk}
+
+
+@inputstep("Verify checklist completion", assignee=Assignee.SYSTEM)
+def verify_complete_checklist() -> FormGenerator:
+    """Show a form for the operator to input a link to the completed checklist."""
+
+    class VerifyCompleteForm(FormPage):
+        info_label: Label = "Verify that the checklist has been completed. Then continue this workflow."  # type: ignore[assignment]
+        checklist_url: str = ""
+
+    user_input = yield VerifyCompleteForm
+
+    return {"checklist_url": user_input.dict()["checklist_url"]}
+
+
+@workflow(
+    "Activate an IP Trunk",
+    initial_input_form=wrap_modify_initial_input_form(_initial_input_form),
+    target=Target.MODIFY,
+)
+def activate_iptrunk() -> StepList:
+    """Move an IP Trunk from a ``PROVISIONING`` state to an ``ACTIVE`` state.
+
+    * Send email notifications to different teams.
+    * Wait for approval to be given.
+    * Update the subscription lifecycle state to ``ACTIVE``.
+    """
+    return (
+        init
+        >> store_process_subscription(Target.MODIFY)
+        >> unsync
+        >> verify_complete_checklist
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/test/fixtures.py b/test/fixtures.py
index bc96381bfa605c3934610eac8490fa275e6051de..32bbfdca03b7218df8e6ce36b7f184f1390668c5 100644
--- a/test/fixtures.py
+++ b/test/fixtures.py
@@ -226,6 +226,7 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker):
         iptrunk_ipv4_network=None,
         iptrunk_ipv6_network=None,
         iptrunk_sides=None,
+        status: SubscriptionLifecycle | None = None,
     ) -> UUIDstr:
         product_id = subscriptions.get_product_id_by_name(ProductType.IP_TRUNK)
         description = description or faker.sentence()
@@ -255,6 +256,10 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker):
             iptrunk_subscription,
             SubscriptionLifecycle.ACTIVE,
         )
+
+        if status:
+            iptrunk_subscription.status = status
+
         iptrunk_subscription.description = description
         iptrunk_subscription.start_date = start_date
         iptrunk_subscription.save()
diff --git a/test/workflows/iptrunk/test_activate_iptrunk.py b/test/workflows/iptrunk/test_activate_iptrunk.py
new file mode 100644
index 0000000000000000000000000000000000000000..837f340c6adc77e1aa148760e425429288e4d01f
--- /dev/null
+++ b/test/workflows/iptrunk/test_activate_iptrunk.py
@@ -0,0 +1,36 @@
+import pytest
+
+from gso.products import Iptrunk
+from test.workflows import (
+    assert_complete,
+    assert_suspended,
+    extract_state,
+    resume_workflow,
+    run_workflow,
+)
+
+
+@pytest.mark.workflow()
+def test_activate_router_success(
+    iptrunk_subscription_factory,
+    faker,
+):
+    #  Set up mock return values
+    product_id = iptrunk_subscription_factory(status="provisioning")
+    #  Sanity check
+    assert Iptrunk.from_subscription(product_id).status == "provisioning"
+
+    #  Run workflow
+    initial_input_data = [{"subscription_id": product_id}, {}]
+    result, process_stat, step_log = run_workflow("activate_iptrunk", initial_input_data)
+
+    assert_suspended(result)
+    result, step_log = resume_workflow(process_stat, step_log, input_data=[{"checklist_url": "http://localhost"}])
+
+    assert_complete(result)
+
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = Iptrunk.from_subscription(subscription_id)
+
+    assert subscription.status == "active"