diff --git a/gso/migrations/versions/2023-11-02_259c320235f5_add_site_modification_and_termination_.py b/gso/migrations/versions/2023-11-02_259c320235f5_add_site_modification_and_termination_.py
new file mode 100644
index 0000000000000000000000000000000000000000..32a9db91d428494340cf1c7678360fcac567d71e
--- /dev/null
+++ b/gso/migrations/versions/2023-11-02_259c320235f5_add_site_modification_and_termination_.py
@@ -0,0 +1,33 @@
+"""Add Site modification and termination workflow..
+
+Revision ID: 259c320235f5
+Revises: 394dc60d5c02
+Create Date: 2023-11-02 10:12:09.778614
+
+"""
+import sqlalchemy as sa
+from alembic import op
+from orchestrator.migrations.helpers import create_workflow, delete_workflow
+
+# revision identifiers, used by Alembic.
+revision = "259c320235f5"
+down_revision = "394dc60d5c02"
+branch_labels = None
+depends_on = None
+
+new_workflows = [
+    {"name": "modify_site", "target": "MODIFY", "description": "Modify site", "product_type": "Site"},
+    {"name": "terminate_site", "target": "TERMINATE", "description": "Terminate site", "product_type": "Site"},
+]
+
+
+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/services/subscriptions.py b/gso/services/subscriptions.py
index c6c3ffaf89ad6c503cb67b7a65c282f7ae6df1ec..cffa645fc5eb949f9425798e3319c6af10ff50f8 100644
--- a/gso/services/subscriptions.py
+++ b/gso/services/subscriptions.py
@@ -87,22 +87,25 @@ def get_product_id_by_name(product_name: ProductType) -> UUID:
     return ProductTable.query.filter_by(name=product_name).first().product_id
 
 
-def get_active_site_subscription_by_name(site_name: str) -> Subscription:
-    """Retrieve an active subscription for a site by the site's name.
+def get_active_subscriptions_by_field_and_value(field_name: str, field_value: str) -> list[Subscription]:
+    """Retrieve a list of active subscriptions based on a specified field and its value.
 
-    :param site_name: The name of the site for which to retrieve the subscription.
-    :type site_name: str
+    :param field_name: The name of the field to filter by.
+    :type field_name: str
 
-    :return: The Subscription object for the site.
-    :rtype: Subscription
+    :param field_value: The value of the field to match against.
+    :type field_value: Any
+
+    :return: A list of active Subscription objects that match the criteria.
+    :rtype: List[Subscription]
     """
     return (
         SubscriptionTable.query.join(ProductTable)
         .join(SubscriptionInstanceTable)
         .join(SubscriptionInstanceValueTable)
         .join(ResourceTypeTable)
-        .filter(SubscriptionInstanceValueTable.value == site_name)
-        .filter(ResourceTypeTable.resource_type == "site_name")
+        .filter(SubscriptionInstanceValueTable.value == field_value)
+        .filter(ResourceTypeTable.resource_type == field_name)
         .filter(SubscriptionTable.status == SubscriptionLifecycle.ACTIVE)
-        .first()
+        .all()
     )
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index 319103e04b2fd1190d367ca9df4725c07b549eac..08269f6186d65845ca11db33d4e0a84d1f4fc71b 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -1,7 +1,9 @@
+import ipaddress
 import re
 from ipaddress import IPv4Address
 from uuid import UUID
 
+import pycountry
 from orchestrator import step
 from orchestrator.types import State, UUIDstr
 from pydantic import BaseModel
@@ -13,6 +15,7 @@ from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
 from gso.services import provisioning_proxy
 from gso.services.netbox_client import NetboxClient
+from gso.services.subscriptions import get_active_subscriptions_by_field_and_value
 
 
 class LAGMember(BaseModel):
@@ -151,3 +154,28 @@ def validate_iptrunk_unique_interface(interfaces: list[LAGMember]) -> list[LAGMe
     if len(interface_names) != len(set(interface_names)):
         raise ValueError("Interfaces must be unique.")
     return interfaces
+
+
+def validate_site_fields_is_unique(field_name: str, value: str | int) -> str | int:
+    """Validate that a site field is unique."""
+    if len(get_active_subscriptions_by_field_and_value(field_name, str(value))) > 0:
+        raise ValueError(f"{field_name} must be unique")
+    return value
+
+
+def validate_ipv4_or_ipv6(value: str) -> str:
+    """Validate that a value is a valid IPv4 or IPv6 address."""
+    try:
+        ipaddress.ip_address(value)
+        return value
+    except ValueError:
+        raise ValueError("Enter a valid IPv4 or IPv6 address.")
+
+
+def validate_country_code(country_code: str) -> str:
+    """Validate that a country code is valid."""
+    try:
+        pycountry.countries.lookup(country_code)
+        return country_code
+    except LookupError:
+        raise ValueError("Invalid or non-existent country code, it must be in ISO 3166-1 alpha-2 format.")
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index 65a84b5d64e63f09074d2da0e7cd9660322e92ae..451afaa9477aa557cd5a5e005b5a8ab958238f2d 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -9,6 +9,8 @@ LazyWorkflowInstance("gso.workflows.iptrunk.terminate_iptrunk", "terminate_iptru
 LazyWorkflowInstance("gso.workflows.router.create_router", "create_router")
 LazyWorkflowInstance("gso.workflows.router.terminate_router", "terminate_router")
 LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
+LazyWorkflowInstance("gso.workflows.site.modify_site", "modify_site")
+LazyWorkflowInstance("gso.workflows.site.terminate_site", "terminate_site")
 LazyWorkflowInstance("gso.workflows.tasks.import_site", "import_site")
 LazyWorkflowInstance("gso.workflows.tasks.import_router", "import_router")
 LazyWorkflowInstance("gso.workflows.tasks.import_iptrunk", "import_iptrunk")
diff --git a/gso/workflows/site/create_site.py b/gso/workflows/site/create_site.py
index 429b0d7e489be69a980050c6b4698cddd1a1dd10..ac509adf3203c3b00543e3b7e5ae7094fd740582 100644
--- a/gso/workflows/site/create_site.py
+++ b/gso/workflows/site/create_site.py
@@ -1,7 +1,3 @@
-import ipaddress
-from typing import NoReturn
-
-import pycountry
 from orchestrator.forms import FormPage
 from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
@@ -9,11 +5,13 @@ from orchestrator.workflow import StepList, done, init, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
 from pydantic import validator
+from pydantic.fields import ModelField
 
 from gso.products.product_blocks import site as site_pb
 from gso.products.product_blocks.site import LatitudeCoordinate, LongitudeCoordinate
 from gso.products.product_types import site
 from gso.services.crm import customer_selector
+from gso.utils.helpers import validate_country_code, validate_ipv4_or_ipv6, validate_site_fields_is_unique
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:  # noqa: C901
@@ -33,21 +31,20 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:  # noqa: C
         site_tier: site_pb.SiteTier
         site_ts_address: str
 
+        @validator("site_ts_address", allow_reuse=True)
+        def validate_ts_address(cls, site_ts_address: str) -> str:
+            validate_site_fields_is_unique("site_ts_address", site_ts_address)
+            validate_ipv4_or_ipv6(site_ts_address)
+            return site_ts_address
+
         @validator("site_country_code", allow_reuse=True)
-        def country_code_must_exist(cls, country_code: str) -> str | NoReturn:
-            try:
-                _ = pycountry.countries.lookup(country_code)
-                return country_code
-            except LookupError:
-                raise ValueError("Invalid or non-existent country code, it must be in ISO 3166-1 alpha-2 format.")
+        def country_code_must_exist(cls, country_code: str) -> str:
+            validate_country_code(country_code)
+            return country_code
 
-        @validator("site_ts_address", allow_reuse=True)
-        def ts_address_must_be_valid(cls, ts_address: str) -> str | NoReturn:
-            try:
-                ipaddress.ip_address(ts_address)
-                return ts_address
-            except ValueError:
-                raise ValueError("Enter a valid IPv4 or v6 address.")
+        @validator("site_name", "site_internal_id", "site_bgp_community_id", allow_reuse=True)
+        def validate_unique_fields(cls, value: str, field: ModelField) -> str | int:
+            return validate_site_fields_is_unique(field.name, value)
 
     user_input = yield CreateSiteForm
 
diff --git a/gso/workflows/site/modify_site.py b/gso/workflows/site/modify_site.py
new file mode 100644
index 0000000000000000000000000000000000000000..15f1c6b45a3826b20a227bdd24b5361c456946fe
--- /dev/null
+++ b/gso/workflows/site/modify_site.py
@@ -0,0 +1,89 @@
+from orchestrator.forms import FormPage
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
+from orchestrator.workflow import StepList, done, init, step, workflow
+from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
+from orchestrator.workflows.utils import wrap_modify_initial_input_form
+from pydantic import validator
+from pydantic.fields import ModelField
+from pydantic_forms.core import ReadOnlyField
+
+from gso.products.product_blocks import site as site_pb
+from gso.products.product_blocks.site import LatitudeCoordinate, LongitudeCoordinate
+from gso.products.product_types.site import Site
+from gso.utils.helpers import validate_ipv4_or_ipv6, validate_site_fields_is_unique
+
+
+def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
+    subscription = Site.from_subscription(subscription_id)
+
+    class ModifySiteForm(FormPage):
+        class Config:
+            title = "Modify Site"
+
+        site_name: str = ReadOnlyField(subscription.site.site_name)
+        site_city: str = subscription.site.site_city
+        site_country: str = ReadOnlyField(subscription.site.site_country)
+        site_country_code: str = ReadOnlyField(subscription.site.site_country_code)
+        site_latitude: LatitudeCoordinate = subscription.site.site_latitude
+        site_longitude: LongitudeCoordinate = subscription.site.site_longitude
+        site_bgp_community_id: int = subscription.site.site_bgp_community_id
+        site_internal_id: int = subscription.site.site_internal_id
+        site_tier: site_pb.SiteTier = ReadOnlyField(subscription.site.site_tier)
+        site_ts_address: str | None = subscription.site.site_ts_address
+
+        @validator("site_ts_address", allow_reuse=True)
+        def validate_ts_address(cls, site_ts_address: str) -> str:
+            if site_ts_address and site_ts_address != subscription.site.site_ts_address:
+                validate_site_fields_is_unique("site_ts_address", site_ts_address)
+                validate_ipv4_or_ipv6(site_ts_address)
+            return site_ts_address
+
+        @validator("site_internal_id", "site_bgp_community_id", allow_reuse=True)
+        def validate_unique_fields(cls, value: str, field: ModelField) -> str | int:
+            if value == getattr(subscription.site, field.name):
+                return value
+            return validate_site_fields_is_unique(field.name, value)
+
+    user_input = yield ModifySiteForm
+
+    return user_input.dict()
+
+
+@step("Modify subscription")
+def modify_site_subscription(
+    subscription: Site,
+    site_city: str,
+    site_latitude: LatitudeCoordinate,
+    site_longitude: LongitudeCoordinate,
+    site_bgp_community_id: int,
+    site_internal_id: int,
+    site_ts_address: str,
+) -> State:
+    subscription.site.site_city = site_city
+    subscription.site.site_latitude = site_latitude
+    subscription.site.site_longitude = site_longitude
+    subscription.site.site_bgp_community_id = site_bgp_community_id
+    subscription.site.site_internal_id = site_internal_id
+    subscription.site.site_ts_address = site_ts_address
+
+    subscription.description = f"Site in {site_city}, {subscription.site.site_country}"
+
+    return {"subscription": subscription}
+
+
+@workflow(
+    "Modify Site",
+    initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
+    target=Target.MODIFY,
+)
+def modify_site() -> StepList:
+    return (
+        init
+        >> store_process_subscription(Target.MODIFY)
+        >> unsync
+        >> modify_site_subscription
+        >> set_status(SubscriptionLifecycle.ACTIVE)
+        >> resync
+        >> done
+    )
diff --git a/gso/workflows/site/terminate_site.py b/gso/workflows/site/terminate_site.py
new file mode 100644
index 0000000000000000000000000000000000000000..73a99e22e0d32a7abf0d1dda1314d80a6b911d7d
--- /dev/null
+++ b/gso/workflows/site/terminate_site.py
@@ -0,0 +1,35 @@
+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
+
+from gso.products.product_types.site import Site
+
+
+def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
+    Site.from_subscription(subscription_id)
+
+    class TerminateForm(FormPage):
+        termination_label: Label = "Are you sure you want to delete this site?"  # type: ignore[assignment]
+
+    user_input = yield TerminateForm
+    return user_input.dict()
+
+
+@workflow(
+    "Terminate Site",
+    initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
+    target=Target.TERMINATE,
+)
+def terminate_site() -> StepList:
+    return (
+        init
+        >> store_process_subscription(Target.TERMINATE)
+        >> unsync
+        >> set_status(SubscriptionLifecycle.TERMINATED)
+        >> resync
+        >> done
+    )
diff --git a/gso/workflows/tasks/import_iptrunk.py b/gso/workflows/tasks/import_iptrunk.py
index 9c36d62c06c0b197a1df3a0fe290aaae8c771c75..4be9b2c273745da35335b5cce127f9c8a45488a0 100644
--- a/gso/workflows/tasks/import_iptrunk.py
+++ b/gso/workflows/tasks/import_iptrunk.py
@@ -28,7 +28,7 @@ def _generate_routers() -> dict[str, str]:
 
 def initial_input_form_generator() -> FormGenerator:
     routers = _generate_routers()
-    RouterEnum = Choice("Select a router", zip(routers.keys(), routers.items()))  # type: ignore[arg-type]
+    router_enum = Choice("Select a router", zip(routers.keys(), routers.items()))  # type: ignore[arg-type]
 
     class CreateIptrunkForm(FormPage):
         class Config:
@@ -41,12 +41,12 @@ def initial_input_form_generator() -> FormGenerator:
         iptrunk_speed: PhyPortCapacity
         iptrunk_minimum_links: int
 
-        side_a_node_id: RouterEnum  # type: ignore[valid-type]
+        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_members: UniqueConstrainedList[LAGMember]
 
-        side_b_node_id: RouterEnum  # type: ignore[valid-type]
+        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_members: UniqueConstrainedList[LAGMember]
diff --git a/gso/workflows/tasks/import_router.py b/gso/workflows/tasks/import_router.py
index d7984b9264af68bed7815126bee445d29062daea..517261452a81250f7684a02e7660671f3125ada3 100644
--- a/gso/workflows/tasks/import_router.py
+++ b/gso/workflows/tasks/import_router.py
@@ -25,7 +25,7 @@ def _get_site_by_name(site_name: str) -> Site:
     ----
     site_name (str): The name of the site.
     """
-    subscription = subscriptions.get_active_site_subscription_by_name(site_name)
+    subscription = subscriptions.get_active_subscriptions_by_field_and_value("site_name", site_name)[0]
     if not subscription:
         raise ValueError(f"Site with name {site_name} not found.")
 
diff --git a/test/conftest.py b/test/conftest.py
index 5616e3b6e6bb01e29490eb038714328b4c0e52c4..25c830dc64dd2167f134ea094e659158029489f4 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -126,7 +126,7 @@ def configuration_data() -> dict:
         }
 
 
-@pytest.fixture(scope="session")
+@pytest.fixture(scope="session", autouse=True)
 def data_config_filename(configuration_data) -> str:
     file_name = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
     open(file_name, "x").close()
diff --git a/test/workflows/site/test_modify_site.py b/test/workflows/site/test_modify_site.py
new file mode 100644
index 0000000000000000000000000000000000000000..00475d7ef2ed93e998f336e52f6e61fc8c726346
--- /dev/null
+++ b/test/workflows/site/test_modify_site.py
@@ -0,0 +1,44 @@
+import pytest
+from pydantic_forms.exceptions import FormValidationError
+
+from gso.products.product_types.site import Site
+from test.workflows import assert_complete, extract_state, run_workflow
+
+
+@pytest.mark.workflow
+def test_modify_site(responses, site_subscription_factory):
+    subscription_id = site_subscription_factory()
+    initial_site_data = [
+        {"subscription_id": subscription_id},
+        {
+            "site_bgp_community_id": 10,
+            "site_internal_id": 20,
+            "site_ts_address": "127.0.0.1",
+        },
+    ]
+    result, process, step_log = run_workflow("modify_site", initial_site_data)
+    assert_complete(result)
+
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = Site.from_subscription(subscription_id)
+    assert "active" == subscription.status
+    assert subscription.site.site_bgp_community_id == initial_site_data[1]["site_bgp_community_id"]
+    assert subscription.site.site_internal_id == initial_site_data[1]["site_internal_id"]
+
+
+@pytest.mark.workflow
+def test_modify_site_with_invalid_data(responses, site_subscription_factory):
+    subscription_a = Site.from_subscription(site_subscription_factory())
+    subscription_b = Site.from_subscription(site_subscription_factory())
+
+    initial_site_data = [
+        {"subscription_id": subscription_b.subscription_id},
+        {
+            "site_bgp_community_id": subscription_a.site.site_bgp_community_id,
+        },
+    ]
+
+    with pytest.raises(FormValidationError) as e:
+        run_workflow("modify_site", initial_site_data)
+        assert "site_bgp_community_id must be unique" in str(e.value)
diff --git a/test/workflows/site/test_terminate_site.py b/test/workflows/site/test_terminate_site.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc88b10273a12bbaed779715b0bb45d976a84432
--- /dev/null
+++ b/test/workflows/site/test_terminate_site.py
@@ -0,0 +1,17 @@
+import pytest
+
+from gso.products.product_types.site import Site
+from test.workflows import assert_complete, extract_state, run_workflow
+
+
+@pytest.mark.workflow
+def test_terminate_site(responses, site_subscription_factory):
+    subscription_id = site_subscription_factory()
+    initial_site_data = [{"subscription_id": subscription_id}, {}]
+    result, process, step_log = run_workflow("terminate_site", initial_site_data)
+    assert_complete(result)
+
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = Site.from_subscription(subscription_id)
+    assert "terminated" == subscription.status