> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-integration/3.0/troubleshooting-and-resources/tutorials-and-code-samples/ci-windows.md).

# Microsoft Windows

You can build and test a [Microsoft Windows](https://learn.microsoft.com/en-us/windows/apps/get-started/?tabs=net-maui%2Ccpp-win32) application using a Windows platform on [Harness Cloud](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md) or a [self-managed Kubernetes cluster](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure/run-windows-builds-in-a-kubernetes-build-infrastructure.md) build infrastructure.

This guide assumes you've [created a Harness CI pipeline](/continuous-integration/3.0/use-harness-ci/use-harness-ci/prep-ci-pipeline-components.md).

### Specify architecture <a href="#specify-architecture" id="specify-architecture"></a>

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
stages:
  - stage:
      name: build
      identifier: build
      type: CI
      spec:
        cloneCodebase: true
        platform:
          os: Windows
          arch: Amd64
        runtime:
          type: Cloud
          spec: {}
```

{% endtab %}

{% tab title="Self-managed" %}
There are several self-managed build infrastructure options. This example uses a Kubernetes cluster build infrastructure. For instructions and important information, go to [Run Windows builds in a Kubernetes cluster build infrastructure](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure/run-windows-builds-in-a-kubernetes-build-infrastructure.md).

```yaml
stages:
  - stage:
      name: build
      identifier: build
      description: ""
      type: CI
      spec:
        cloneCodebase: true
        infrastructure:
          type: KubernetesDirect
          spec:
            connectorRef: YOUR_K8S_CLUSTER_CONNECTOR_ID
            namespace: YOUR_K8S_NAMESPACE
            automountServiceAccountToken: true
            nodeSelector:
              kubernetes.io/os: windows
            os: Windows
```

{% endtab %}
{% endtabs %}

### Install dependencies <a href="#install-dependencies" id="install-dependencies"></a>

{% tabs %}
{% tab title="Harness Cloud" %}
Harness Cloud runners include pre-installed libraries and tools, and you can use [Run steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md) to install additional dependencies or additional versions. For details about pre-installed tools and versions, go to [Platforms and image specifications](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md#platforms-and-image-specifications).

```yaml
- step:
    type: Run
    identifier: dependencies
    name: Dependencies
    spec:
      shell: Powershell
      command: |-
        dotnet add package Newtonsoft.json --version 12.0.1
```

{% endtab %}

{% tab title="Self-managed" %}
You can use [Run steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md) to install dependencies.

```yaml
- step:
    type: Run
    identifier: dependencies
    name: Dependencies
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/dotnet/sdk:7.0
      shell: Powershell
      command: |-
        dotnet add package Newtonsoft.json --version 12.0.1
```

{% endtab %}
{% endtabs %}

### Cache dependencies <a href="#cache-dependencies" id="cache-dependencies"></a>

Add caching to your Build stage.

{% tabs %}
{% tab title="Cache Intelligence" %}
Cache your Windows app dependencies with [Cache Intelligence](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/cache-intelligence.md). Add caching to your `stage.spec` and specify the `paths` to cache:

```yaml
- stage:
    spec:
      caching:
        enabled: true
        key: cache-{{ checksum "packages.lock.json" }}
        paths:
          - C:\%LocalAppData%\NuGet\Cache
      sharedPaths:
        - C:\%LocalAppData%\NuGet\Cache
```

{% endtab %}

{% tab title="Save and Restore Cache steps" %}
You can use built-in steps to:

* [Save and Restore Cache from S3](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/saving-cache.md)
* [Save and Restore Cache from GCS](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/save-cache-in-gcs.md)

<details>

<summary>YAML example: Save and restore cache steps</summary>

Here's an example of a pipeline with **Save Cache to S3** and **Restore Cache from S3** steps.

```yaml
            steps:
              - step:
                  type: RestoreCacheS3
                  name: Restore Cache From S3
                  identifier: Restore_Cache_From_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1
                    bucket: YOUR_S3_BUCKET
                    key: cache-{{ checksum "packages.lock.json" }}
                    archiveFormat: Tar
              - step:
                  type: Run
                  ...
              - step:
                  type: BuildAndPushDockerRegistry
                  ...
              - step:
                  type: SaveCacheS3
                  name: Save Cache to S3
                  identifier: Save_Cache_to_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1
                    bucket: YOUR_S3_BUCKET
                    key: cache-{{ checksum "packages.lock.json" }}
                    sourcePaths:
                      - C:\%LocalAppData%\NuGet\Cache
                    archiveFormat: Tar
```

</details>
{% endtab %}
{% endtabs %}

### Build and run tests <a href="#build-and-run-tests" id="build-and-run-tests"></a>

Add [Run steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md) to build and run your tests.

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
- step:
    type: Run
    identifier: build_dotnet_app
    name: Build DotNet App
    spec:
      shell: Powershell
      command: |-
        dotnet restore
        dotnet build --no-restore
        dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
```

{% endtab %}

{% tab title="Self-managed" %}

```yaml
- step:
    type: Run
    identifier: build_dotnet_app
    name: Build DotNet App
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/dotnet/sdk:6.0
      shell: Powershell
      command: |-
        dotnet restore
        dotnet build --no-restore
        dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
For some languages, you can leverage Harness' [Test Intelligence](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/ti-overview.md) feature to reduce unit test time.

Harness CI also supports [test splitting (parallelism)](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/speed-up-ci-test-pipelines-using-parallelism.md) for both **Run** and **Test** steps.
{% endhint %}

#### Visualize test results <a href="#visualize-test-results" id="visualize-test-results"></a>

You can [view test results](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/viewing-tests.md) on the **Tests** tab of your pipeline executions. Harness supports JUnit XML and TRX test report formats. TRX reports are automatically converted to JUnit XML.

For your pipeline to produce test reports, you need to modify the **Run** step that runs your tests. Make sure the `command` generates test reports and add the `reports` specification.

For .NET projects, you can generate TRX reports natively using `dotnet test -l:trx` without needing a converter. If your test tool does not produce JUnit XML or TRX formatted reports by default, you can use a converter to output compatible JUnit XML reports, such as [NUnit to JUnit](https://github.com/nunit/nunit-transforms/tree/master/nunit3-junit) or [.NET trx2JUnit](https://github.com/gfoidl/trx2junit).

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
- step:
    type: Run
    identifier: install_converter
    name: install converter
    spec:
      shell: Powershell
      command: |-
        dotnet tool install -g trx2junit
        export PATH="C:\Users\USER\.dotnet\tools"
- step:
    type: Run
    identifier: build_dotnet_app
    name: Build DotNet App
    spec:
      shell: Powershell
      command: |-
        dotnet restore
        dotnet build
        dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
        trx2junit results.trx
      reports:
        type: JUnit
        spec:
          paths:
            - results.xml
```

{% endtab %}

{% tab title="Self-managed" %}

```yaml
- step:
    type: Run
    identifier: install_converter
    name: install converter
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/dotnet/sdk:6.0
      shell: Powershell
      command: |-
        dotnet tool install -g trx2junit
        export PATH="C:\Users\USER\.dotnet\tools"
- step:
    type: Run
    identifier: build_dotnet_app
    name: Build DotNet App
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/dotnet/sdk:6.0
      shell: Powershell
      command: |-
        dotnet restore
        dotnet build
        dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
        trx2junit results.trx
      reports:
        type: JUnit
        spec:
          paths:
            - results.xml
```

{% endtab %}
{% endtabs %}

### Install Visual Studio <a href="#install-visual-studio" id="install-visual-studio"></a>

{% tabs %}
{% tab title="Harness Cloud" %}
Visual Studio 2019 Enterprise is pre-installed on Hosted Cloud runners. For details about all available tools and versions, go to [Platforms and image specifications](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md#platforms-and-image-specifications).

You can use a **Run** step to install a different version or edition of Visual Studio.

```yaml
- step:
    type: Run
    identifier: install_vs2022
    name: install vs2022
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/windows/servercore:ltsc2019
      shell: Powershell
      command: |-
        winget install --id Microsoft.VisualStudio.2022.Enterprise
```

{% endtab %}

{% tab title="Self-managed" %}
If not already included on your build machine, you can specify a container image that has the necessary binaries or use a **Run** step to install Visual Studio.

```yaml
- step:
    type: Run
    identifier: install_vs2022
    name: install vs2022
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/windows/servercore:ltsc2019
      shell: Powershell
      command: |-
        winget install --id Microsoft.VisualStudio.2019.Enterprise
```

{% endtab %}
{% endtabs %}

### Specify shell <a href="#specify-shell" id="specify-shell"></a>

{% tabs %}
{% tab title="Harness Cloud" %}
In steps that allow you to supply your own commands, such as [**Run** steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md#shell-and-command) and [**Background** steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/manage-dependencies/background-step-settings.md#shell-entry-point-and-command), you specify the `shell` in the step's settings.

```yaml
- step:
    type: Run
    identifier: dotnet restore
    name: dotnet restore
    spec:
      shell: Powershell ## Set to Bash, Powershell, Pwsh (PowerShell Core), Python, or Sh.
      command: |- ## Enter your script as you would in a command line shell.
        dotnet restore
```

Several shell binaries are pre-installed on Hosted Cloud runners, including Bash and PowerShell. For details about all available tools and versions, go to [Platforms and image specifications](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md#platforms-and-image-specifications).

You can also use **Run** steps to install different shell tools into the build environment, or specify a container image that has the necessary binaries for the command you want to run.
{% endtab %}

{% tab title="Self-managed" %}
In steps that allow you to supply your own commands, such as [**Run** steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md#shell-and-command) and [**Background** steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/manage-dependencies/background-step-settings.md#shell-entry-point-and-command), you specify the `shell` in the step's settings.

```yaml
- step:
    type: Run
    identifier: build_dotnet_app
    name: Build DotNet App
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: mcr.microsoft.com/dotnet/sdk:6.0
      shell: Powershell ## Set to Bash, Powershell, Pwsh (PowerShell Core), Python, or Sh.
      command: |- ## Enter your script as you would in a command line shell.
        dotnet restore
```

You can also use **Run** steps to install different shell tools into the build environment, or specify a container image that has the necessary binaries for the command you want to run.
{% endtab %}
{% endtabs %}

### Setup .NET SDK <a href="#setup-net-sdk" id="setup-net-sdk"></a>

For details about building and testing .NET with Harness CI, including how to setup different versions of the .NET SDK, go to the [C# (.NET Core) guide](/continuous-integration/troubleshooting-and-resources/tutorials-and-code-samples/ci-dotnet.md).

### Full pipeline examples <a href="#full-pipeline-examples" id="full-pipeline-examples"></a>

The following full pipeline examples are based on the partial examples above.

{% tabs %}
{% tab title="Harness Cloud" %}
If you copy this example, replace the placeholder values with appropriate values for your [code repo connector](/continuous-integration/3.0/use-harness-ci/use-harness-ci/codebase-configuration/create-and-configure-a-codebase.md#code-repo-connectors) and repository name. Depending on your project and organization, you may also need to replace `projectIdentifier` and `orgIdentifier`.

```yaml
pipeline:
  name: default
  identifier: default
  projectIdentifier: default
  orgIdentifier: default
  tags: {}
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  stages:
    - stage:
        name: build and test
        identifier: build_and_test
        type: CI
        spec:
          cloneCodebase: true
          caching:
            enabled: true
            key: cache-{{ checksum "packages.lock.json" }}
            paths:
              - C:\%LocalAppData%\NuGet\Cache
          execution:
            steps:
              - step:
                  type: Run
                  identifier: dependencies
                  name: Dependencies
                  spec:
                    shell: Powershell
                    command: |-
                      dotnet add package Newtonsoft.json --version 12.0.1
              - step:
                  type: Run
                  identifier: install_converter
                  name: install converter
                  spec:
                    shell: Powershell
                    command: |-
                      dotnet tool install -g trx2junit
                      export PATH="C:\Users\USER\.dotnet\tools"
              - step:
                  type: Run
                  identifier: build_dotnet_app
                  name: Build DotNet App
                  spec:
                    shell: Powershell
                    command: |-
                      dotnet restore
                      dotnet build
                      dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
                      trx2junit results.trx
                    reports:
                      type: JUnit
                      spec:
                        paths:
                          - results.xml
          sharedPaths:
            - C:\%LocalAppData%\NuGet\Cache
          platform:
            os: Windows
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
```

{% endtab %}

{% tab title="Self-managed" %}
If you copy this example, replace the placeholder values with appropriate values for your [code repo connector](/continuous-integration/3.0/use-harness-ci/use-harness-ci/codebase-configuration/create-and-configure-a-codebase.md#code-repo-connectors), [Kubernetes cluster connector](/harness-ai/use-harness-platform/connectors/cloud-providers/add-a-kubernetes-cluster-connector.md), Kubernetes namespace, and repository name. Depending on your project and organization, you may also need to replace `projectIdentifier` and `orgIdentifier`.

```yaml
pipeline:
  name: default
  identifier: default
  projectIdentifier: default
  orgIdentifier: default
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  tags: {}

pipeline:
  name: default
  identifier: default
  projectIdentifier: default
  orgIdentifier: default
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  tags: {}
  stages:
    - stage:
        name: build and test
        identifier: build_and_test
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: RestoreCacheS3
                  name: Restore Cache From S3
                  identifier: Restore_Cache_From_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1
                    bucket: YOUR_S3_BUCKET
                    key: cache-{{ checksum "packages.lock.json" }}
                    archiveFormat: Tar
              - step:
                  type: Run
                  identifier: dependencies
                  name: Dependencies
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: mcr.microsoft.com/dotnet/sdk:7.0
                    shell: Powershell
                    command: |-
                      dotnet add package Newtonsoft.json --version 12.0.1
              - step:
                  type: Run
                  identifier: install_converter
                  name: install converter
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: mcr.microsoft.com/dotnet/sdk:6.0
                    shell: Powershell
                    command: |-
                      dotnet tool install -g trx2junit
                      export PATH="C:\Users\USER\.dotnet\tools"
              - step:
                  type: Run
                  identifier: build_dotnet_app
                  name: Build DotNet App
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: mcr.microsoft.com/dotnet/sdk:6.0
                    shell: Powershell
                    command: |-
                      dotnet restore
                      dotnet build
                      dotnet test C:\path\to\project.tests.csproj --no-build --verbosity normal
                      trx2junit results.trx
                    reports:
                      type: JUnit
                      spec:
                        paths:
                          - results.xml
              - step:
                  type: SaveCacheS3
                  name: Save Cache to S3
                  identifier: Save_Cache_to_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1
                    bucket: YOUR_S3_BUCKET
                    key: cache-{{ checksum "packages.lock.json" }}
                    sourcePaths:
                      - C:\%LocalAppData%\NuGet\Cache
                    archiveFormat: Tar
          infrastructure:
            type: KubernetesDirect
            spec:
              connectorRef: YOUR_K8S_CLUSTER_CONNECTOR_ID
              namespace: YOUR_K8S_NAMESPACE
              automountServiceAccountToken: true
              nodeSelector:
                kubernetes.io/os: windows
              os: Windows
```

{% endtab %}
{% endtabs %}

### Next steps <a href="#next-steps" id="next-steps"></a>

Now that you have created a pipeline that builds and tests a Windows app, you could:

* Create [triggers](/harness-ai/use-harness-platform/triggers.md) to automatically run your pipeline.
* Add steps to [build and upload artifacts](/continuous-integration/3.0/use-harness-ci/use-harness-ci/build-and-upload-artifacts.md).
* Add a step to [build and push an image to a Docker registry](/continuous-integration/3.0/use-harness-ci/use-harness-ci/build-and-upload-artifacts/build-and-push/build-and-push-to-docker-registry.md).
