MSBuild
current directory
MSBuild script
directory path
build automation

How can I get the current directory in an MSBuild script?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In MSBuild, "current directory" can mean different things depending on what you actually need. Sometimes you want the directory of the project file, sometimes the directory of an imported .targets file, and sometimes the process working directory from which MSBuild was launched.

Use the Right Built-In Property

The most commonly useful properties are:

  • '$(MSBuildProjectDirectory) for the current project file'
  • '$(MSBuildThisFileDirectory) for the file currently being evaluated'
  • '$(MSBuildStartupDirectory) for the directory where the build was started'

Those are not interchangeable, so the first step is choosing the one that matches your intent. That distinction matters because many MSBuild path bugs come from using a project-relative property when the real need was import-relative or invocation-relative behavior.

Project File Directory

If you want the directory containing the .csproj or .proj file, use $(MSBuildProjectDirectory).

xml
1<Project>
2  <Target Name="ShowProjectDir">
3    <Message Text="Project dir: $(MSBuildProjectDirectory)" Importance="high" />
4  </Target>
5</Project>

This is usually the correct choice for paths relative to the project itself.

Directory of the Current .targets or .props File

If you are authoring a reusable imported file, $(MSBuildThisFileDirectory) is often safer.

xml
1<Project>
2  <Target Name="ShowThisFileDir">
3    <Message Text="This file dir: $(MSBuildThisFileDirectory)" Importance="high" />
4  </Target>
5</Project>

This matters when a .targets file is imported from many projects. In that case, MSBuildProjectDirectory points to the consuming project, while MSBuildThisFileDirectory points to the imported file's location.

Startup Directory

If you truly want the directory from which the build was launched, use $(MSBuildStartupDirectory).

xml
1<Project>
2  <Target Name="ShowStartupDir">
3    <Message Text="Startup dir: $(MSBuildStartupDirectory)" Importance="high" />
4  </Target>
5</Project>

That is useful for tooling scenarios, but it is usually less stable than project-relative paths because it depends on how the build was invoked.

Use Property Functions for the Process Working Directory

If you need the actual process current directory, you can also call .NET directly from an MSBuild property function.

xml
1<Project>
2  <PropertyGroup>
3    <ProcessCurrentDirectory>$([System.IO.Directory]::GetCurrentDirectory())</ProcessCurrentDirectory>
4  </PropertyGroup>
5
6  <Target Name="ShowProcessCurrentDirectory">
7    <Message Text="Process current dir: $(ProcessCurrentDirectory)" Importance="high" />
8  </Target>
9</Project>

This is helpful when you want the OS-level working directory specifically, but it also means you are tying the script to invocation context instead of build-file location.

Which One Should You Actually Use

A practical rule is:

  • use MSBuildProjectDirectory for project-relative assets
  • use MSBuildThisFileDirectory inside imported .props or .targets
  • use MSBuildStartupDirectory only when launch location really matters
  • use GetCurrentDirectory() only when you need the process working directory explicitly

In build automation, deterministic file-relative paths are usually better than invocation-relative paths. That is why reusable build logic should usually prefer MSBuildThisFileDirectory or MSBuildProjectDirectory over shell-dependent working-directory assumptions.

Example: Build Output Relative to the Project

xml
1<Project>
2  <PropertyGroup>
3    <ArtifactsDir>$(MSBuildProjectDirectory)\artifacts</ArtifactsDir>
4  </PropertyGroup>
5
6  <Target Name="ShowArtifactsDir">
7    <Message Text="Artifacts dir: $(ArtifactsDir)" Importance="high" />
8  </Target>
9</Project>

This is more reliable than assuming the current shell directory always matches the project root.

Common Pitfalls

The biggest mistake is saying "current directory" without deciding whether you mean project location, imported file location, or process working directory. MSBuild exposes all three concepts separately.

Another issue is using MSBuildStartupDirectory for project-relative paths. That works only as long as every build is launched from the same place.

A third problem is using MSBuildProjectDirectory inside reusable imported targets when the real intent was "path relative to this imported file."

Summary

  • In MSBuild, several different directory concepts exist, and they solve different problems.
  • Use $(MSBuildProjectDirectory) for the current project file location.
  • Use $(MSBuildThisFileDirectory) inside imported build files.
  • Use $(MSBuildStartupDirectory) only when launch location matters.
  • Prefer stable file-relative paths over process-working-directory assumptions.

Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.