# How to Extend MSBuild Through Custom Tasks and Extension Points

> Learn to extend MSBuild with custom tasks and extension points. Inject custom logic using AfterTargets and BeforeTargets in .targets files for greater build control.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: how-to-guide
- Published: 2026-07-07

---

**Extend MSBuild by creating custom tasks that derive from `Microsoft.Build.Utilities.Task` and injecting them via extension points using `AfterTargets` and `BeforeTargets` attributes in imported `.targets` files.**

The **dotnet/skills** repository provides concrete reference implementations showing how to extend MSBuild through custom tasks and extension points. By leveraging the patterns found in the `plugins/dotnet-msbuild/skills` directory, you can inject custom build logic without modifying core project files or risking build instability.

## Understanding MSBuild Custom Tasks

A **custom task** is a .NET class that either derives from `Microsoft.Build.Utilities.Task` or implements `Microsoft.Build.Framework.ITask`. These tasks execute arbitrary code during the build process, enabling operations like code generation, file transformation, or external tool invocation.

### Creating a Task Class

Custom tasks override the `Execute()` method and expose properties using MSBuild-specific attributes. In the `target-authoring` skill located at `plugins/dotnet-msbuild/skills/target-authoring/`, the repository demonstrates tasks that compile into standalone DLLs and accept inputs via `[Required]` and `[Output]` attributes.

The task assembly must target the appropriate .NET version compatible with your MSBuild installation. Once compiled, the DLL becomes available for registration within your build scripts.

### Registering Tasks with UsingTask

Before invoking a custom task, you must register it using the `<UsingTask>` element. According to the reference patterns in `HardTarget.csproj`, the registration maps the task name to its assembly location:

```xml
<UsingTask TaskName="HardTask" 
           AssemblyFile="$(MSBuildThisFileDirectory)HardTask.dll" />

```

This declaration typically resides in a `.targets` file that consuming projects import. The `$(MSBuildThisFileDirectory)` property ensures the path resolves relative to the targets file location, making the extension portable across different machines and CI environments.

## Leveraging MSBuild Extension Points

**Extension points** are predefined targets in the MSBuild lifecycle that allow injection of custom logic without overriding existing behavior. The `dotnet/skills` repository emphasizes patterns that append functionality rather than replacing it.

### The HardExtension Pattern

The `HardExtension.csproj` file in `plugins/dotnet-msbuild/skills/extension-points/hard/` demonstrates the minimal viable approach to extending builds. This pattern creates a separate target that hooks into the build pipeline without modifying the original project file's logic.

Rather than overriding core targets like `Build` or `Compile`, the HardExtension approach uses independent targets that execute in relation to existing ones. This preserves the original build semantics while adding your custom steps.

### Safe Target Injection with AfterTargets

The **Chain Extension** pattern illustrated in the [`target-authoring/SKILL.md`](https://github.com/dotnet/skills/blob/main/target-authoring/SKILL.md) documentation shows how to append targets without overwriting. Use the `AfterTargets` or `BeforeTargets` attributes to specify execution order:

```xml
<Target Name="CustomValidation" 
        AfterTargets="Build" 
        DependsOnTargets="CoreCompile">
  <HardTask Input="$(TargetPath)" 
            Output="$(IntermediateOutputPath)validated.txt" />
</Target>

```

This configuration guarantees that `CustomValidation` runs immediately after the `Build` target completes, while the `DependsOnTargets` attribute ensures compilation finishes first. The repository explicitly warns against directly overriding targets like `CoreBuild`, as this breaks downstream projects that rely on standard MSBuild behavior.

## Practical Implementation Patterns

The `dotnet/skills` repository organizes its MSBuild extensions into distinct skills demonstrating specific patterns:

- **Target Authoring** (`plugins/dotnet-msbuild/skills/target-authoring/`): Shows how to create reusable targets and custom tasks, including the HardTask implementation and chain extension techniques.

- **Property Patterns** (`plugins/dotnet-msbuild/skills/property-patterns/`): Demonstrates centralizing common MSBuild properties to avoid duplication across large solutions.

- **Extension Points** (`plugins/dotnet-msbuild/skills/extension-points/`): Contains `ExtensionPoints.csproj`, which provides reusable targets that can be imported by multiple solutions.

### Complete Extension Example

To implement a custom extension in your project, create a `MyExtensions.targets` file:

```xml
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="MyCustomTask" 
             AssemblyFile="$(MSBuildThisFileDirectory)MyCustomTask.dll" />
  
  <Target Name="RunMyTask" 
          AfterTargets="Build" 
          BeforeTargets="AfterBuild">
    <MyCustomTask Input="$(ProjectDir)src\myfile.txt" 
                  Output="$(IntermediateOutputPath)myfile.processed.txt" />
  </Target>
</Project>

```

Import this file from your main project:

```xml
<Import Project="MyExtensions.targets" />

```

## Best Practices for MSBuild Extensions

When extending MSBuild through the patterns found in the `dotnet/skills` repository, follow these guidelines to maintain build reliability:

- **Never overwrite core targets** – Always use `AfterTargets` or `BeforeTargets` rather than redefining existing targets like `Build` or `Compile`. Overwriting can break incremental builds and downstream dependencies.

- **Keep tasks side-effect-free** – Tasks that modify source files should be deterministic. Non-deterministic tasks confuse MSBuild's incremental build system, causing unnecessary rebuilds or missed updates.

- **Isolate extensions in .targets files** – Place custom logic in imported `.targets` files rather than directly in `.csproj` files. This promotes reusability across projects and keeps project files clean.

- **Use MSBuild property functions for paths** – Reference task assemblies using `$(MSBuildThisFileDirectory)` to ensure paths resolve correctly regardless of the build working directory.

## Summary

- **Custom tasks** are .NET classes implementing `ITask` or extending `Task`, compiled into DLLs and registered via `<UsingTask>` elements.
- The **HardExtension pattern** in `plugins/dotnet-msbuild/skills/extension-points/hard/HardExtension.csproj` demonstrates safe injection of build logic without overwriting existing targets.
- Use **`AfterTargets`** and **`BeforeTargets`** attributes to hook into the build lifecycle at specific points.
- Place extensions in **reusable `.targets` files** imported by consuming projects to maintain clean project files and enable sharing across solutions.
- Reference task assemblies using **`$(MSBuildThisFileDirectory)`** to ensure portable path resolution.

## Frequently Asked Questions

### What is the difference between custom tasks and extension points?

**Custom tasks** are compiled .NET classes that execute specific logic during builds, while **extension points** are hook locations in the MSBuild target graph where you can inject those tasks or other targets. You create a custom task to perform work (like file processing), then use extension points (via `AfterTargets`/`BeforeTargets`) to schedule when that work executes without modifying core build files.

### How do I reference a custom task assembly in MSBuild?

Register the task using the `<UsingTask>` element with the `AssemblyFile` attribute pointing to your compiled DLL. As shown in `plugins/dotnet-msbuild/skills/target-authoring/HardTarget.csproj`, use `$(MSBuildThisFileDirectory)` to make the path relative to the importing targets file: `<UsingTask TaskName="HardTask" AssemblyFile="$(MSBuildThisFileDirectory)HardTask.dll" />`.

### Can I overwrite existing MSBuild targets safely?

**No.** The `dotnet/skills` repository explicitly recommends against overwriting core targets like `Build` or `CoreCompile`. Instead, use `AfterTargets` or `BeforeTargets` to append your logic. The **Chain Extension** pattern ensures your target runs while preserving the original behavior, preventing breaks in downstream projects that depend on standard MSBuild behavior.

### Where can I find working examples of MSBuild extensions?

The **dotnet/skills** repository contains working examples in `plugins/dotnet-msbuild/skills/`. Key files include `target-authoring/HardTarget.csproj` for custom task implementation, `extension-points/hard/HardExtension.csproj` for safe extension patterns, and `extension-points/ExtensionPoints.csproj` for reusable target libraries. The [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) files in these directories provide narrative explanations of the implementation patterns.