diff --git a/shared/workspaces/test/test_workflow_service.py b/shared/workspaces/test/test_workflow_service.py
index 3f770b747b654a4db97e3efb96503f9d0142d392..3a8f2f9a2d6e1c97413fc39c4cc41d06d888cf0e 100644
--- a/shared/workspaces/test/test_workflow_service.py
+++ b/shared/workspaces/test/test_workflow_service.py
@@ -37,7 +37,12 @@ from messaging.messenger import SenderIF
 from mock_alchemy.mocking import UnifiedAlchemyMagicMock
 
 from workspaces.capability.services.capability_service import Router
-from workspaces.workflow.schema import Workflow, WorkflowRequest, WorkflowRequestFile
+from workspaces.workflow.schema import (
+    Workflow,
+    WorkflowRequest,
+    WorkflowRequestFile,
+    WorkflowTemplate,
+)
 from workspaces.workflow.services.interfaces import WorkflowInfoIF
 from workspaces.workflow.services.workflow_info import WorkflowInfo
 from workspaces.workflow.services.workflow_service import WorkflowService
@@ -414,3 +419,11 @@ class TestWorkflowService:
     def test_get_dagfile_log(self, mock_workflow_service: WorkflowService):
         log_path = mock_workflow_service._get_dag_logfile_name(pathlib.Path("fake.dag"))
         assert log_path.name == "fake.dag.dagman.log"
+
+
+def test_condor_name_utility():
+    t = WorkflowTemplate()
+    t.filename = "foo.dag"
+    t.content = b"{{#make_condor_jobid}}T10t10/J2030+2093.23{{/make_condor_jobid}}"
+    file = t.render({})
+    assert file.content == b"T10t10/J2030-2093-23"
diff --git a/shared/workspaces/workspaces/workflow/schema.py b/shared/workspaces/workspaces/workflow/schema.py
index f422d9eb6be2716c36a3d210ea25a45baef158a9..2ad5457628c25c5aff78db2decbfd958136934ca 100644
--- a/shared/workspaces/workspaces/workflow/schema.py
+++ b/shared/workspaces/workspaces/workflow/schema.py
@@ -17,7 +17,7 @@
 # along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
 import datetime
 import logging
-from typing import Dict, List
+from typing import Callable, Dict, List
 
 import chevron
 import pendulum
@@ -118,6 +118,11 @@ class WorkflowTemplate(JSONSerializable):
         :param argument:    argument(s) needed for template render
         :return:            an AbstractFile rendered from mustache template
         """
+        # add some rendering helpers to the argument
+        render_helpers = {"make_condor_jobid": self.make_condor_jobid}
+        argument = {**render_helpers, **argument}
+
+        # render
         contents = chevron.render(self.content.decode("ascii"), argument)
         logger.debug(
             "rendering %s with argument %s to %s",
@@ -147,6 +152,12 @@ class WorkflowTemplate(JSONSerializable):
     def from_json(cls, json: dict) -> any:
         return WorkflowTemplate(json["filename"], json["content"])
 
+    @staticmethod
+    def make_condor_jobid(content: str, renderer: Callable[[str], str]):
+        # get the rendered content
+        rendered = renderer(content)
+        return rendered.replace(".", "-").replace("+", "-")
+
     def __repr__(self):
         return f"<WorkflowTemplate filename={self.filename} for workflow={self.workflow_name}>"