Skip to content

secdev02/TextTransformer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TextTransform.exe Demo

A minimal example of running a T4 template with TextTransform.exe, both locally and in a GitHub Actions workflow on windows-latest.

Screenshot 2026-07-16 at 8 51 13 PM

Contents

templates/PrintDateTest.tt        The T4 template
.github/workflows/texttransform.yml   GitHub Action that runs the transform

The template

templates/PrintDateTest.tt:

<#@ template language="C#" #>
<#@ output extension=".txt" #>
<#@ import namespace="System" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Windows.Forms" #>
Current date: <#= DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") #>

The <#@ assembly #> and <#@ import #> directives for System.Windows.Forms are included so you can extend this template with UI calls (MessageBox.Show(...), etc) if you want to experiment locally. Note that anything which opens a modal dialog will hang a CI run, since there's nobody there to click it. Keep interactive calls out of anything that runs unattended in the workflow.

Running it locally

windows-latest-hosted GitHub runners, and most developer machines with Visual Studio installed, have TextTransform.exe under the Visual Studio install directory, not under Common Files. Locate it with vswhere, or point at it directly if you know your install path and edition:

& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\TextTransform.exe" -out PrintDate.txt templates\PrintDateTest.tt

Adjust the year and edition (Community, Professional, Enterprise) to match your install. If you don't know the exact path, vswhere.exe (ships with the Visual Studio Installer) will find it for you:

$vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
$vsPath = & $vswhere -latest -products * -property installationPath
& "$vsPath\Common7\IDE\TextTransform.exe" -out PrintDate.txt templates\PrintDateTest.tt

The GitHub Action

.github/workflows/texttransform.yml runs on windows-latest, which ships with Visual Studio pre-installed, so no separate install step is needed. The workflow:

  1. Checks out the repo.
  2. Uses vswhere.exe to find the installed Visual Studio path (this avoids hardcoding a specific year or edition, which can change as GitHub updates its runner images).
  3. Runs TextTransform.exe against templates/PrintDateTest.tt, producing PrintDate.txt.
  4. Prints the output to the workflow log.
  5. Uploads PrintDate.txt as a workflow artifact.

It triggers on push to main, and can also be run manually from the Actions tab (workflow_dispatch).

Why vswhere instead of a hardcoded path

Hardcoding something like:

C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\TextTransform.exe

works until GitHub updates the runner image to a newer Visual Studio year or a different edition, at which point the path silently stops existing and the job fails. vswhere is Microsoft's own tool for finding VS installs by querying the installer's registered state, so it stays correct across runner image updates without you needing to track version numbers.

Alternative: cross-platform, no Visual Studio required

If you'd rather not depend on a Visual Studio install being present on the runner at all (for example, to run on ubuntu-latest, or on a self-hosted runner without VS), see dotnet-t4, an open-source, cross-platform reimplementation of the T4 engine distributed as a .NET tool:

runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-dotnet@v4
    with:
      dotnet-version: '8.0.x'
  - run: dotnet tool install -g dotnet-t4
  - run: ~/.dotnet/tools/t4 -o PrintDate.txt templates/PrintDateTest.tt

This example intentionally sticks with TextTransform.exe itself, since that's what was asked for, but it's worth knowing the alternative exists if the Visual Studio dependency ever becomes a problem.

Security considerations

T4 templates are not a passive data format like JSON or YAML. A .tt file contains real C# (or VB.NET), and TextTransform.exe compiles and executes that code on the machine that runs it. Treat .tt files with the same level of scrutiny as any other executable source code in your repo, not as configuration or content.

Arbitrary code execution, by design. Anything a C# program can do, a T4 template can do: read and write files, make network calls, spawn processes, load arbitrary assemblies via <#@ assembly #>. There's no sandbox. If a .tt file is malicious, running it through TextTransform.exe is equivalent to running an unreviewed .exe.

CI is a bigger blast radius than a laptop. A GitHub Actions runner typically has access to GITHUB_TOKEN, and potentially other repo or org secrets depending on how the workflow is configured. A malicious or compromised .tt template running in that job can exfiltrate those secrets, push commits, or reach out to arbitrary network endpoints, all under the job's permissions. Treat template code changes with the same review bar you'd apply to changes to the workflow file itself.

Be careful with pull_request vs pull_request_target on forks. If this workflow (or one like it) is ever changed to trigger on pull requests from forks, avoid pull_request_target combined with checking out and running the fork's code. That combination is a well-known way to leak secrets, since pull_request_target runs with the base repo's secrets available, but checks out and executes untrusted code from the fork. Plain pull_request from forks does not expose secrets by default, which is safer if external contributions are expected.

Least privilege for the job. Only grant the workflow the permissions it actually needs. If it doesn't need to write to the repo or call the GitHub API, set permissions: contents: read (or narrower) explicitly at the workflow or job level rather than relying on the default token scope.

Review generated output, not just the template. Since the output is produced by executing code, don't assume it's safe just because the .tt file "looks fine" on a skim. Small, easy-to-miss changes to a <#= ... #> expression block can change what the template actually does at generation time.

Avoid interactive calls in anything unattended. As mentioned above, calls like MessageBox.Show(...) will hang a CI job waiting for input that will never come. Beyond the reliability issue, treat any template that references UI, process-launching, or networking APIs (System.Diagnostics.Process, System.Net.*, System.Windows.Forms, etc) as worth a second reviewer's eyes before it merges.

Know the tool's support status. TextTransform.exe is a legacy, .NET Framework-era component bundled with Visual Studio; it isn't actively evolving the way the SDK-integrated tooling is. If you're evaluating this for long-term use in a team's CI, it's worth weighing that against a maintained, open-source alternative like dotnet-t4 (see above), which also has the advantage of being easier to pin to a specific version rather than depending on whatever the runner's Visual Studio install happens to be.

Summary for reviewers: if a pull request touches a .tt file, review it like you would a .cs file with build-time execution, not like a template or config change.

About

More Than Meets the Eye

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors