> 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-delivery/use-continuous-delivery/deploy-services-on-different-platforms/traditional/win-rm-tutorial.md).

# Windows VM deployments using WinRM

You can use Windows Remote Management (WinRM) to deploy your artifacts to Windows virtual machine (VM) or bare-metal hosts located in Microsoft Azure, AWS, or any platform-agnostic Physical Data Center (PDC). Harness connects to your target Windows instances using the WinRM protocol and executes PowerShell commands to deploy your artifact.

WinRM deployments support both generic artifact deployment and Internet Information Services (IIS)-specific deployment types. When you select an execution strategy, you can choose from four artifact types: **IIS Application**, **IIS Virtual Directory**, **IIS Website**, or **Other** (generic). Each IIS artifact type comes with a built-in PowerShell command template that handles application pool configuration, site setup, and artifact deployment on the target host.

For WinRM, you can access artifacts from [Harness Artifact Registry](/continuous-delivery/use-continuous-delivery/cd-building-blocks/services/artifact-sources.md#harness-artifact-registry) (**Docker**, **Generic**, **NPM**, **NuGet**, **Maven**, and **Raw** artifact types), **Jenkins**, **Artifactory**, **Custom**, **Nexus**, and **Amazon S3**.

The **Execution Strategies** supported for WinRM include **Blank Canvas**, **Basic**, **Rolling**, and **Canary**.

Supported security protocols for WinRM include Kerberos and Windows New Technology LAN Manager (NTLM).

Enable [Harness File Store](/continuous-delivery/use-continuous-delivery/cd-building-blocks/services/add-inline-manifests-using-file-store.md) to upload config files from the file store.

***

### What will you learn in this topic? <a href="#what-will-you-learn-in-this-topic" id="what-will-you-learn-in-this-topic"></a>

* How to [create the deploy stage](#create-the-deploy-stage) for WinRM deployments.
* How to [create a Harness WinRM service](#create-a-harness-winrm-service) and [add an Artifactory connector](#add-the-artifactory-connector).
* How to [configure the environment and infrastructure](#configure-the-environment-and-infrastructure) for PDC, Azure, or AWS targets.
* How to [use a Basic deployment](#use-a-basic-deployment-for-winrm-execution) for WinRM execution.
* How to run [IIS deployments with WinRM](#iis-deployments-with-winrm) using the Rolling, Canary, or Basic strategy.
* How to [run the pipeline to deploy and review](#run-the-pipeline-to-deploy-and-review) the result.

***

### Before you begin <a href="#before-you-begin" id="before-you-begin"></a>

Before you set up a WinRM deployment, make sure you have the following:

* **Harness key concepts**: Go to [Harness key concepts](/harness-ai/new-to-harness-platform/overview.md) to establish a general understanding of Harness.
* **Available delegate**: Make sure you have a delegate available in your environment. You can install a Kubernetes or Docker delegate, ideally in the same subnet as the target hosts.
* **Target host**: This topic uses an AWS EC2 instance as the target host.
* **EC2 instance credentials**: To configure and authenticate your WinRM credentials using NTLM, you need the domain name, username, and password for the EC2 instance.
* **IIS service**: To use Harness execution strategies, make sure you have the [IIS service](https://learn.microsoft.com/en-us/iis/application-frameworks/scenario-build-an-aspnet-website-on-iis/configuring-step-1-install-iis-and-asp-net-modules#to-install-iis-and-aspnet-modules-on-windows-server-2012-using-the-ui) installed on your machine.
* **OIDC-enabled AWS connector (optional)**: Harness supports WinRM with an **OIDC-enabled AWS connector**, but it requires delegate version `854xx` or later. Go to [AWS OIDC connector reference](/harness-ai/use-harness-platform/connectors/cloud-providers/ref-cloud-providers/aws-connector-settings-reference.md) to review the requirements.

***

### Create the deploy stage <a href="#create-the-deploy-stage" id="create-the-deploy-stage"></a>

Pipelines are collections of stages. In this topic, you create a new pipeline and add a stage for WinRM deployments of artifacts to a target host.

Perform the following steps to create the deploy stage:

1. In your Harness project, click **Pipelines**, and then **Create a Pipeline**.
2. Enter a name for the pipeline: `winrm-tutorial`. Keep the default **Inline (Pipeline is stored in Harness)** for **How do you want to set up your pipeline**.
3. Click **Start** and your pipeline appears.
4. Click **Add Stage**.
5. For **Select Stage Type**, select **Deploy**.
6. For **Stage Name**, enter `winrm-stage`.
7. For **Deployment Type**, select **WinRM** and click **Set Up Stage**.

<figure><img src="/files/ntvgoTbSTuwT67sRkyuV" alt="Select WinRM as the deployment type and set up the stage"><figcaption><p>Click to view full size image</p></figcaption></figure>

***

### Create a Harness WinRM service <a href="#create-a-harness-winrm-service" id="create-a-harness-winrm-service"></a>

Next, create a Harness service that represents your application. After you create a service, it is persistent and you can use it throughout the stages of this or any other pipeline in the project.

The Harness WinRM service contains the application package artifact (file or metadata) and the related config files to deploy on the target host.

Perform the following steps to create the service for an application artifact:

1. For **Select Service**, click **New Service,** enter a name for the service: `winrm-service`. You use this name when selecting this service in Harness environments.
2. For **Service Definition**, in **Deployment Type**, select **WinRM** and click **Continue.**

#### Add the Artifactory connector <a href="#add-the-artifactory-connector" id="add-the-artifactory-connector"></a>

Harness includes connectors for all the major artifact repositories. This topic uses a `todolist.zip` file available in a public Harness Artifactory repository.

1. In **Artifacts**, click **Add Primary Artifact**.
2. In **Specify Artifact Repository Type**, select **Artifactory** and click **Continue**.
3. For the Artifactory Connector, select **New Artifactory Connector**.
4. In **Name**, enter a name for the connector: `artifactory-connector` and click **Continue**.
5. In **Details**, enter the following URL path for **Artifactory Repository URL** or click the tooltip and copy the URL: `https://harness.jfrog.io/artifactory`. This topic uses the artifacts stored in that repository.
6. For **Authentication**, click the down-drop arrow for **Username and Password**. Then, select **Anonymous (no credentials required)**. Click **Continue**.

<figure><img src="/files/qfukLGxlcEZwgPCvaXYH" alt="Configure anonymous authentication for the Artifactory connector"><figcaption><p>Click to view full size image</p></figcaption></figure>

7. Click **Continue** to connect with Artifactory using a Harness delegate.
8. In **Delegates Setup**, retain the default selection: **Use any available delegate**.
9. Click **Save and Continue**.
10. In **Connection Test**, Harness validates the Artifactory repository authentication and permissions for the repository. Click **Continue**. If the test fails, that means the delegate cannot connect to `https://harness.jfrog.io/artifactory/`. Make sure that the EC2 instance hosting the delegate can make outbound connections to `https://harness.jfrog.io/artifactory/`.

#### Set up artifact location and details <a href="#set-up-artifact-location-and-details" id="set-up-artifact-location-and-details"></a>

This topic uses a **ToDo List** app artifact, `todolist.zip`, available in a public Harness Artifactory repository.

1. In **Artifact Details**, for **Repository Format**, keep the default value **Generic**.
2. For **Repository**, enter `todolist-tutorial`. If you select the down-drop menu for **Repository**, Harness loads any available repositories and displays them for selection.
3. For **Artifact Directory**, enter a forward slash `/`.
4. For **Artifact Details**, keep the default **Value**.
5. For **Artifact Path**, leave the default runtime input value `<+input>` for that field. Click **Submit.** The **Artifactory Connector** is added to **Artifacts.** Click **Continue**.

{% hint style="info" %}
The artifact [expression](/continuous-delivery/use-continuous-delivery/cd-building-blocks/services/artifact-sources.md) may vary based on the chosen artifact type.
{% endhint %}

***

### Configure the environment and infrastructure <a href="#configure-the-environment-and-infrastructure" id="configure-the-environment-and-infrastructure"></a>

You configure the environment and infrastructure for your deployment in the **Environment** settings of the pipeline stage.

To set up the environment, select **New Environment**, provide a name for the environment (for example, `winrm-env`), choose the environment type (**Production** or **Pre-Production**), and click **Save**. This step is common across all infrastructure types.

Next, configure the infrastructure definition. There are two methods of specifying the deployment target infrastructure:

* **Pre-existing**: The target infrastructure already exists and you need to provide the required settings.
* **Dynamically provisioned**: The target infrastructure is dynamically provisioned as part of the deployment process.

Go to [Provisioning overview](/continuous-delivery/use-continuous-delivery/provision-infrastructure/provisioning-overview.md) to understand Harness provisioning.

WinRM deployments support three infrastructure types:

* **Physical Data Center (PDC)**: Deploy to on-premise or cloud-hosted Windows servers managed outside of Azure or AWS.
* **Azure**: Deploy to Windows VMs in Microsoft Azure subscriptions.
* **AWS**: Deploy to Windows EC2 instances in Amazon Web Services.

#### Physical Data Center <a href="#physical-data-center" id="physical-data-center"></a>

The Physical Data Center (PDC) infrastructure type targets Windows servers that are not managed through Azure or AWS connectors. When you select PDC as the infrastructure type, Harness presents two host configuration modes: **Single Host** and **Host Groups**.

**Single Host**

The **Single Host** tab is the default configuration mode where all hosts share a single WinRM credential.

<figure><img src="/files/aNDRExT52YH2RWa0ufxy" alt="Single Host configuration mode for a PDC infrastructure"><figcaption><p>Click to view full size image</p></figcaption></figure>

When you set up the Infrastructure Definition, there are three options for selecting target hosts:

* **Specify hosts**: Manually enter a comma-separated list of host names.
* **Select preconfigured hosts from Physical Data Center**: Use a PDC connector to pull registered hosts, with optional filtering by host name or host attributes.
* **Map Dynamically Provisioned Infrastructure**: Map outputs from a provisioner (Terraform, CloudFormation, and so on) to resolve hosts at runtime. Go to [Dynamically provisioned infrastructure](#dynamically-provisioned-infrastructure) to map provisioner outputs and resolve hosts at runtime.

{% hint style="info" %}
When specifying hosts inline, enter them as a comma-separated list.
{% endhint %}

**Filter hosts by attributes**

This setting is available when you pick **Select preconfigured hosts from Physical Data Center** under **Select hosts** and select **Filter by host attributes**.

You can control whether multiple host-attribute filters are combined with **OR (match any)** or **AND (match all)** logic:

* **Any (default)**: A host is selected if at least one filter condition matches (existing behavior).
* **All**: A host is selected only if every filter condition matches.
* **Specific Attribute**: The condition that the match criteria is checked against. You can specify attributes like region, type, and name.

  <figure><img src="/files/Sm6w5et3PA4MOwHZAMnr" alt="Specify host attributes and match criteria for filtering"><figcaption><p>Click to view full size image</p></figcaption></figure>

<details>

<summary>Example of how filtering hosts by attributes works</summary>

Suppose you have two hosts specified in your physical data center

```json
// Host-1
{
  "hostname": "ec2-00-00-01.compute-1.amazonaws.com",
  "hostAttribute": {
    "region": "us-east-1",
    "name": "node-1"
  }
}

// Host-2
{
  "hostname": "ec2-00-00-02.compute-1.amazonaws.com",
  "hostAttribute": {
    "region": "us-west",
    "name": "node-5"
  }
}
```

Scenario 1: `matchCriteria: ALL`

**YAML Example for `matchCriteria: ALL`**

```yaml
infrastructureDefinition:
  name: pdc
  identifier: pdc
  orgIdentifier: default
  projectIdentifier: project_id
  environmentRef: env_id
  deploymentType: Ssh
  type: Pdc
  spec:
    connectorRef: abc
    credentialsRef: credentials_ref
    hostFilter:
      type: HostAttributes
      spec:
        value:
          region: us-east-1
          name: node-1
        matchCriteria: ALL
  allowSimultaneousDeployments: false
```

* **matchCriteria: ALL** means that **both** conditions must be true for the host to be selected (both `region=us-east-1` **and** `name=node-1`).

**Host-1**

* `region`: us-east-1 (matches)
* `name`: node-1 (matches)

**Selected**: Host-1 meets both conditions, so it **matches**.

**Host-2**

* `region`: us-west (does not match `us-east-1`)
* `name`: node-5 (does not match `node-1`)

**Not Selected**: Host-2 does not meet **both** conditions, so it **does not match**.

**Summary for `ALL`**:

* **Host-1** is selected because both `region` and `name` match.
* **Host-2** is not selected because neither `region` nor `name` match.

***

Scenario 2: `matchCriteria: ANY`

**YAML Example for `matchCriteria: ANY`**

```yaml
infrastructureDefinition:
  name: pdc
  identifier: pdc
  orgIdentifier: default
  projectIdentifier: project_id
  environmentRef: env_ref
  deploymentType: Ssh
  type: Pdc
  spec:
    connectorRef: abc
    credentialsRef: credentials_ref
    hostFilter:
      type: HostAttributes
      spec:
        value:
          region: us-east-1
          name: node-1
        matchCriteria: ANY
  allowSimultaneousDeployments: false

```

* **matchCriteria: ANY** means that **either** condition must be true for the host to be selected (either `region=us-east-1` **or** `name=node-1`).

**Host-1**

* `region`: us-east-1 (matches)
* `name`: node-1 (matches)

**Selected**: Host-1 meets both conditions, so it **matches**.

**Host-2**

* `region`: us-west (does not match `us-east-1`)
* `name`: node-5 (does not match `node-1`)

**Not Selected**: Host-2 does not meet either condition, so it **does not match**.

**Summary for `ANY`**:

* **Host-1** is selected because **both** `region` and `name` match.
* **Host-2** is not selected because neither `region` nor `name` matches.

***

Key Takeaways:

* **matchCriteria: ALL**: The host is selected only if **both** conditions are true.
* **matchCriteria: ANY**: The host is selected if **either** of the conditions is true.

</details>

**Set up a pre-existing PDC infrastructure**

The following is an example Infrastructure Definition for a pre-existing PDC infrastructure. After you create your environment, continue with the infrastructure setup.

Perform the following steps to create the infrastructure:

1. For **Specify Infrastructure**, click **New infrastructure**.
2. Enter a name for the new infrastructure: `winrm-infra`.
3. For **Select Infrastructure Type**, select **Physical Data Center**.
4. Scroll down to view the Infrastructure Definition.
5. Keep the default selection: **Select preconfigured hosts from Physical Data Center**.

**Create the PDC connector for the host**

1. In **Infrastructure Definition**, for **Connector**, click **Select Connector** to create the connector for PDC.
2. In **Create or Select an Existing Connector**, select **New Connector**.
3. For **Name**, enter `pdc-connector` and click **Continue**.

<figure><img src="/files/816ZPhcgAJJ9lOtvj72v" alt="Name the new PDC connector"><figcaption><p>Click to view full size image</p></figcaption></figure>

4. In **Details**, keep the default selection for **Manually enter host names**. In **Hosts**, enter the host name for your EC2 instance and click **Continue**.

<figure><img src="/files/Dfpk4dnwILcwgieD97wq" alt="Enter the EC2 host name for the PDC connector"><figcaption><p>Click to view full size image</p></figcaption></figure>

5. In **Delegates Setup**, keep the default for **Use any available delegate**. Click **Save** and **Continue**.
6. In **Test Connection**, Harness validates the connectivity for the PDC host. Click **Finish**.

<figure><img src="/files/sIeUGQhSE7CwP0DBU72f" alt="Successful PDC host connection test"><figcaption><p>Click to view full size image</p></figcaption></figure>

**Use WinRM credentials with NTLM to authenticate**

1. For **Specify Credentials**, click **Create or Select a Secret**.
2. In **Create or Select an Existing Secret**, click **New WinRm Credential**.
3. In **WinRm Details**, enter a name for this credential.
4. For **Select an Auth Scheme**, keep the default **NTLM**.
5. For **Domain**, enter the domain name for your EC2 instance.
6. For **Username**, enter the username used to access the EC2 instance.
7. For **Password**, click **Create or Select a Secret**.
8. In **Create or Select an Existing Secret**, click **New Secret Text**.
9. In **Add new Encrypted text**, enter a name for this secret. This is the name you will use to reference the text elsewhere in your resources.
10. In **Secret Value**, enter the password that you use to access the EC2 instance. Click **Save** and **Password** is populated with the secret you created.
11. Click the checkboxes for **Use SSL** and **Skip Cert Check**. Leave the **Use No Profile** checkbox empty.
12. For **WinRM Port**, keep the default port number **5986**. Click **Save and Continue**.
13. For **Add a Host Name to start Verification**, enter the hostname for your EC2 instance and click **Test Connection**. Harness checks for Delegates and verifies the connection to the target host.
14. In **Create New Infrastructure**, click **Preview Hosts**.
15. Click the checkbox for the host and click **Test Connection**. The WinRM connection to the EC2 instance is tested. Click **Finish**. Click **Continue**.

**Host Groups**

{% hint style="info" %}
This feature is behind the feature flag `CDS_ENABLE_INFRA_HOST_GROUPS`. Contact [Harness Support](mailto:support@harness.io) to enable it.
{% endhint %}

By default, all hosts in a PDC infrastructure share a single WinRM credential. Host Groups remove this limitation by letting you organize hosts into named groups, each with its own credential. This is useful for environments that use Just Enough Administration (JEA), where different server groups require distinct credential and endpoint configurations.

With Host Groups, a single infrastructure definition can target multiple Windows servers using different credentials in the same pipeline execution. For example, your web servers might use one JEA endpoint while your application servers use another. Host Groups let you model this without creating separate infrastructures.

{% hint style="info" %}
Host Groups are supported only for the **Physical Data Center (PDC)** infrastructure type.
{% endhint %}

{% hint style="info" %}
Host Groups currently support only fixed values for host and credential configurations. Runtime inputs and expressions are not supported in Host Groups at this time, although they are available in the Single Host configuration.
{% endhint %}

**Configure Host Groups**

To configure Host Groups, select the **Host Groups** tab in the Infrastructure Definition dialog. This tab appears alongside the existing **Single Host** tab when your infrastructure type is set to Physical Data Center.

<figure><img src="/files/ZFbJAZ84PiWkt0UTA9vn" alt="Host Groups tab in the Infrastructure Definition dialog"><figcaption><p>Click to view full size image</p></figcaption></figure>

Select **+ Add Host Group** to create a new group. You can add multiple host groups to the same infrastructure. Each host group expands into a configuration panel with the following fields:

* **Host Group Key**: A unique identifier for the group. This key is required and distinguishes host groups within the infrastructure definition.
* **Host selection method**: Choose between **Specify hosts** (provide host names inline) or **Select preconfigured hosts from Physical Data Center** (use a PDC connector to pull hosts).
* **Specify Credentials**: Each host group requires exactly one WinRM credential. All hosts within the group share this credential during deployment.

**Specify hosts inline**

Select the **Specify hosts** radio button to manually enter host names. Provide a comma-separated list of host names in the **Hosts** field. This approach works well when you have a known, static set of target servers.

**Use preconfigured hosts from a PDC connector**

Select **Select preconfigured hosts from Physical Data Center** to pull hosts from an existing PDC connector. After selecting a connector, choose one of the following filtering options:

* **Include all hosts**: Deploys to every host registered in the PDC connector.
* **Filter by host name**: Specify a comma-separated list of host names to target a subset of hosts from the connector.
* **Filter by host attributes**: Filter hosts based on key-value attribute pairs (for example, `hostType:DB, region:west`). Set the **Match Criteria** to **Any** (match at least one attribute) or **All** (match every attribute).

After configuring your host group, select **Preview Hosts** to verify which hosts are resolved based on your configuration.

**Host Groups YAML reference**

<details>

<summary>Host Groups YAML example</summary>

Below is an example infrastructure definition using Host Groups. The first group specifies inline hosts, the second targets a single host, and the third uses a PDC connector with an "all hosts" filter.

```yaml
infrastructureDefinition:
  name: infra_name
  identifier: infra_id
  orgIdentifier: default
  projectIdentifier: project_name
  environmentRef: env_name
  deploymentType: WinRm
  type: Pdc
  spec:
    hostGroups:
      - identifier: hostGroup_1
        hosts:
          - host1
          - host2
          - host3
        credentialRef: credential-1
      - identifier: hostGroup_2
        hosts:
          - host4
        credentialRef: credential-2
      - identifier: hostGroup_3
        pdcConnectorRef: pdcRef-1
        credentialRef: credential-3
        hostFilter:
          type: All
          spec: {}
    delegateSelectors: []
  allowSimultaneousDeployments: false
```

Each entry in `hostGroups` supports:

* **`identifier`**: Unique key for the host group (maps to the **Host Group Key** field in the UI).
* **`hosts`**: A list of host names when specifying hosts inline.
* **`pdcConnectorRef`**: Reference to a PDC connector when using preconfigured hosts.
* **`credentialRef`**: The WinRM credential used for all hosts in this group.
* **`hostFilter`**: Filtering configuration when using a PDC connector. Supported types are `All`, `HostNames`, and `HostAttributes`.

</details>

**Use Host Groups with looping strategies**

When your pipeline uses a looping strategy with the `<+stage.output.hosts>` expression, the stage repeats for each resolved host across all host groups. Harness resolves hosts from every group, pairs each host with its group's credential, and executes the deployment step for each host individually.

```yaml
repeat:
  items: <+stage.output.hosts>
```

This means you do not need to change your execution logic. Harness handles the host-to-credential mapping automatically at the infrastructure level.

#### Azure <a href="#azure" id="azure"></a>

To deploy to Windows VMs hosted in Microsoft Azure, select **Azure** as the infrastructure type in your Infrastructure Definition. Harness connects to your Azure subscription and discovers target VMs based on the criteria you configure.

You need the following settings for an Azure WinRM infrastructure:

* **Connector**: Select or create an Azure Cloud Provider connector that has access to your target subscription.
* **Subscription Id**: The Azure subscription containing the target VMs.
* **Resource Group**: The resource group where the VMs reside.
* **Tags (optional)**: Filter VMs by Azure resource tags to target a specific subset of instances.
* **WinRM Credentials**: A WinRM secret (NTLM or Kerberos) that Harness uses to connect to the target VMs.

{% hint style="info" %}
For Azure WinRM deployments, hostname-based expressions are supported if the hostname is available from the VM metadata. You can also use `<+instance.host.privateIp>` and `<+instance.host.publicIp>`.
{% endhint %}

#### AWS <a href="#aws" id="aws"></a>

To deploy to Windows EC2 instances in Amazon Web Services, select **AWS** as the infrastructure type. Harness uses an AWS connector to discover EC2 instances and WinRM credentials to connect to them.

You need the following settings for an AWS WinRM infrastructure:

* **Connector**: Select or create an AWS Cloud Provider connector. Harness supports OIDC-enabled AWS connectors (requires Delegate version `854xx` or later).
* **Region**: The AWS region containing the target EC2 instances.
* **Tags (optional)**: Filter instances by AWS resource tags.
* **WinRM Credentials**: A WinRM secret (NTLM or Kerberos) that Harness uses to connect to the target instances.

Harness calls the [DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) API to retrieve instances both at runtime during the infrastructure step and during instance sync. Your AWS connector's Identity and Access Management (IAM) role must have the `ec2:DescribeInstances` permission. Go to [Example policies for working with the AWS CLI or an AWS SDK](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ExamplePolicies_EC2.html) to review the required IAM policies.

{% hint style="info" %}
For AWS WinRM deployments, hostname-based expressions like `<+instance.hostName>` and `<+instance.host.hostName>` are **not supported**. Use `<+instance.host.privateIp>` or `<+instance.host.publicIp>` instead.
{% endhint %}

#### Dynamically provisioned infrastructure <a href="#dynamically-provisioned-infrastructure" id="dynamically-provisioned-infrastructure"></a>

Harness supports multiple provisioner types, including Terraform, CloudFormation, Shell Script, and so on. Go to [Provision Infrastructure](/continuous-delivery/use-continuous-delivery/provision-infrastructure.md) to explore the available provisioners and configure provisioning based on your requirements.

Here is a summary of the steps to dynamically provision the target infrastructure for a deployment:

1. **Add dynamic provisioning to the Continuous Delivery (CD) stage**:
   1. In a Harness Deploy stage, in **Environment**, enable the option **Provision your target infrastructure dynamically during the execution of your Pipeline**.
   2. Select the type of provisioner that you want to use.

      Harness automatically adds the provisioner steps for the provisioner type you selected.
   3. Configure the provisioner steps to run your provisioning scripts.
   4. Select or create a Harness infrastructure in **Environment**.
2. **Map the provisioner outputs to the Infrastructure Definition**:
   1. In the Harness infrastructure, enable the option **Map Dynamically Provisioned Infrastructure**.
   2. Map the provisioning script/template outputs to the required infrastructure settings.

**Supported provisioners**

The following provisioners are supported for WinRM deployments:

* Terraform
* Terragrunt
* Terraform Cloud
* CloudFormation
* Azure Resource Manager (ARM)
* Azure Blueprint
* Shell Script

**Add dynamic provisioning to the stage**

Perform the following steps to add dynamic provisioning to a Harness pipeline Deploy stage:

1. In a Harness Deploy stage, in **Environment**, enable the option **Provision your target infrastructure dynamically during the execution of your Pipeline**.
2. Select the type of provisioner that you want to use.

   Harness automatically adds the necessary provisioner steps.
3. Set up the provisioner steps to run your provisioning scripts.

For documentation on each of the required steps for the provisioner you selected, go to the following topics:

* Terraform:
  * [Terraform Plan](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-plan-step.md)
  * [Terraform Apply](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/run-a-terraform-plan-with-the-terraform-apply-step.md)
  * [Terraform Rollback](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/rollback-provisioned-infra-with-the-terraform-rollback-step.md). To display the Terraform Rollback step, toggle the **Rollback** setting.
* [Terragrunt](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terragrunt-howtos.md)
* [Terraform Cloud](/continuous-delivery/use-continuous-delivery/provision-infrastructure/terraform-infra/terraform-cloud-deployments.md)
* CloudFormation:
  * [Create Stack](/continuous-delivery/use-continuous-delivery/provision-infrastructure/cloudformation-infra/provision-with-the-cloud-formation-create-stack-step.md)
  * [Delete Stack](/continuous-delivery/use-continuous-delivery/provision-infrastructure/cloudformation-infra/remove-provisioned-infra-with-the-cloud-formation-delete-step.md)
  * [Rollback Stack](/continuous-delivery/use-continuous-delivery/provision-infrastructure/cloudformation-infra/rollback-provisioned-infra-with-the-cloud-formation-rollback-step.md). To display the Rollback Stack step, toggle the **Rollback** setting.
* [Azure Resource Management (ARM)](/continuous-delivery/use-continuous-delivery/provision-infrastructure/azure-arm-provisioning.md)
* [Azure Blueprint](/continuous-delivery/use-continuous-delivery/provision-infrastructure/azure-blueprint-provisioning.md)
* [Shell Script](/continuous-delivery/use-continuous-delivery/provision-infrastructure/shell-script-provisioning.md)

**Map provisioner output**

After you set up dynamic provisioning in the stage, you must map outputs from your provisioning script or template to specific settings in the Harness Infrastructure Definition used in the stage.

Perform the following steps to map the provisioner output:

1. In the same CD Deploy stage where you enabled dynamic provisioning, select or create (**New Infrastructure**) a Harness infrastructure.
2. In the Harness infrastructure, in **Select Infrastructure Type**, select **Physical Data Center**, **Azure**, or **AWS**.
3. In **Select Hosts/Azure/Amazon Web Services Infrastructure Details**, enable the option **Map Dynamically Provisioned Infrastructure**.

   A **Provisioner** setting is added and configured as a runtime input.
4. Map the provisioning script/template outputs to the required infrastructure settings.

To provision the target deployment infrastructure, Harness needs specific infrastructure information from your provisioning script. You provide this information by mapping specific Infrastructure Definition settings in Harness to outputs from your template/script.

For WinRM infrastructures, Harness needs the following settings mapped to outputs:

* Physical Data Center (PDC):
  * **Host Array Path**
  * **Host Data Mapping**: you should map outputs for all of the necessary connection information. For example, if the VM(s) on a cloud platform:
    * hostname
    * privateIp
    * subnetId
    * region
* Azure:
  * **Subscription Id**
  * **Resource Group**
  * **Tag** (optional)
* AWS:
  * **Region**
  * **Tag** (optional)

{% hint style="info" %}
Ensure the mapped settings are set to the **Expression** option.
{% endhint %}

**Host Array Path**

For WinRM PDC deployments, you use the expression `<+provisioner.OUTPUT_NAME>` for the **Host Array Path** setting.

`OUTPUT_NAME` depends on your provisioner outputs configuration. Make sure `OUTPUT_NAME` is an array object.

If the `OUTPUT_NAME` is stored in several output objects, map the full path to it.

The following Terraform provisioner example uses an `OUTPUT_NAME` called `hostInstances`.

For the subsequent **Host Data Mapping** key-value pairs, you use the expression format `<HOST_PROPERTY>`. For example, `<+public_dns>` or `<+privateIp>`, which are part of the Terraform output configuration in the following snippet.

Here is an example:

<figure><img src="/files/dDywrsHgMSyZITv1CAVD" alt="Map provisioner outputs to Host Array Path and Host Data Mapping settings"><figcaption><p>Click to view full size image</p></figcaption></figure>

**Example**

Here is a snippet of a Terraform script that provisions the infrastructure for an AWS EC2 VM WinRM deployment and includes the required outputs for Physical Data Center:

```json

provider "aws" {
  region = "us-east-1"  # Replace with your desired AWS region
}

resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "example" {
  vpc_id     = aws_vpc.example.id
  cidr_block = "10.0.1.0/24"
}

resource "aws_security_group" "example" {
  name_prefix = "example-"
}

resource "aws_instance" "example" {
  ami                    = "ami-xxxxxxxxxxxxxxxxx"  # Replace with the desired Windows AMI ID
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.example.id
  vpc_security_group_ids = [aws_security_group.example.id]

  tags = {
    Name = "ExampleInstance"
  }

  provisioner "remote-exec" {
    inline = [
      "winrm quickconfig -q",
      "winrm set winrm/config/service/auth @{Basic=\"true\"}",
      "winrm set winrm/config/service @{AllowUnencrypted=\"true\"}",
      "winrm set winrm/config/winrs @{MaxMemoryPerShellMB=\"1024\"}",
    ]
  }

  connection {
    type        = "winrm"
    user        = "Administrator"
    password    = "YourPassword"  # Replace with your desired Windows password
    timeout     = "10m"
    insecure    = true
    https       = true
  }
}

output "hostInstances" {
  value = [
    {
      public_dns  = aws_instance.example.public_dns
      privateIp = aws_instance.example.private_ip
      subnetId  = aws_subnet.example.id
      region    = provider.aws.region
    }
  ]
}

```

In the Harness Infrastructure Definition, you map outputs to their corresponding settings using expressions in the format `<+provisioner.OUTPUT_NAME>`, such as `<+provisioner.region>`.

<figure><img src="/files/dDywrsHgMSyZITv1CAVD" alt="Map Terraform outputs to Infrastructure Definition settings using provisioner expressions"><figcaption><p>Click to view full size image</p></figcaption></figure>

**Reference mapped instance properties**

Once you have mapped provisioning script outputs to the stage Infrastructure Definition, you can reference them in **Execution** of the stage.

To reference a mapped output, you use an expression in the format `<+instance.properties.*>`.

For example, here are some **Host Data Mapping** keys and expressions that reference them:

* hostname: `<+instance.properties.hostname>`
* privateIp: `<+instance.properties.privateIp>`
* subnetId: `<+instance.properties.subnetId>`
* region: `<+instance.properties.region>`

***

### Use a Basic deployment for WinRM execution <a href="#use-a-basic-deployment-for-winrm-execution" id="use-a-basic-deployment-for-winrm-execution"></a>

You are now on **Execution Strategies**, where you select a deployment strategy and run your pipeline.

Perform the following steps to use the Basic deployment strategy:

1. In **Execution Strategies**, select **Basic**.
2. For **Package type**, select **todolist.zip** and keep the defaults for the other selections.
3. Click **Use Strategy**. Harness adds the **Deploy** step for execution.
4. In the **Deploy** step that is added to your pipeline, click **Deploy**.

***

### IIS deployments with WinRM <a href="#iis-deployments-with-winrm" id="iis-deployments-with-winrm"></a>

Harness provides built-in support for deploying to Microsoft IIS through WinRM. When you configure an execution strategy, the **Artifact Type** dropdown lets you select an IIS-specific deployment type. Each type ships with a pre-built PowerShell command template that automates application pool management, directory setup, and artifact deployment on the target Windows host.

The Artifact Type dropdown in the execution strategy screen offers four options. Three are IIS-specific, and the fourth is a generic option:

* **IIS Application**: Deploys an application under an existing IIS Website. This is the most common option for deploying ASP.NET web apps to an existing Default Web Site or custom site. Harness creates or updates the application pool and maps the application to the specified physical path.
* **IIS Virtual Directory**: Deploys content to a virtual directory within an IIS Website. Use this when you need to serve static content or a sub-application from a specific URL path without creating a separate application pool.
* **IIS Website**: Deploys a standalone IIS Website with its own bindings and application pool. Choose this when you are setting up a new site from scratch rather than adding to an existing one.
* **Other**: A generic deployment type with no IIS-specific automation. Use this when deploying non-IIS artifacts or when you want full control over the deployment scripts.

#### Create the WinRM service <a href="#create-the-winrm-service" id="create-the-winrm-service"></a>

Before you configure the execution strategy, set up a WinRM service with your IIS deployment artifact.

Perform the following steps to create the WinRM service:

1. In the pipeline stage, under **Service**, select **New Service**.
2. Enter a name for the service (for example, **winrm-service**).
3. For **Deployment Type**, select **WinRM**.
4. Under **Artifacts**, click **+ Add Artifact Source** to configure the connector and artifact details for your IIS application package (`.zip` file). You can use Harness Artifact Registry (Generic format), Artifactory, S3, Nexus, Jenkins, or a Custom artifact source.
5. Under **Config Files**, optionally add configuration files such as `web.config` that should be deployed alongside the artifact. If your `web.config` is already inside the artifact `.zip`, you can skip this.
6. Under **Variables**, define any service-level variables your deployment scripts need.
7. Click **Save**.

{% hint style="info" %}
**QUICK TEST ARTIFACT**

To test the pipeline without building a full application, create a minimal static HTML site. Create an `index.html` with a simple heading and a `web.config` that sets `index.html` as the default document. Zip both files and upload the `.zip` to your artifact registry (for example, Harness Artifact Registry with **Generic** format). IIS will serve this as a working website.
{% endhint %}

<details>

<summary>Service YAML example</summary>

The following YAML shows a WinRM service definition using Harness Artifact Registry with a Generic format artifact. Replace the placeholder values with your own registry and artifact details.

```yaml
service:
  name: winrm-service
  identifier: winrmservice
  serviceDefinition:
    type: WinRm
    spec:
      artifacts:
        primary:
          primaryArtifactRef: <+input>
          sources:
            - identifier: winrm
              type: Har
              spec:
                registryRef: YOUR_REGISTRY
                type: generic
                spec:
                  artifact: iis-hello-app
                  version: "1.0"
                  fileName: iis-hello-app.zip
  gitOpsEnabled: false
```

| Field                    | Description                                                                                                                                    |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `serviceDefinition.type` | Must be `WinRm` for WinRM deployments.                                                                                                         |
| `sources[].type`         | `Har` for Harness Artifact Registry. Other supported values: `ArtifactoryRegistry`, `AmazonS3`, `Nexus3Registry`, `Jenkins`, `CustomArtifact`. |
| `registryRef`            | Reference to your Harness Artifact Registry.                                                                                                   |
| `type`                   | Repository format within the registry. Use `generic` for `.zip` artifacts.                                                                     |
| `artifact`               | The artifact name as uploaded to the registry.                                                                                                 |
| `version`                | The artifact version string.                                                                                                                   |
| `fileName`               | The file name of the artifact in the registry.                                                                                                 |
| `primaryArtifactRef`     | Set to `<+input>` to select the artifact source at pipeline runtime.                                                                           |

</details>

#### Configure the infrastructure <a href="#configure-the-infrastructure" id="configure-the-infrastructure"></a>

Set up the environment and infrastructure definition to target your Windows hosts. Go to the [Configure the environment and infrastructure](#configure-the-environment-and-infrastructure) section to review a detailed walkthrough of each infrastructure type (PDC, Azure, AWS).

Before you configure the infrastructure in Harness, make sure your target Windows host has IIS installed and WinRM enabled. Run these commands on the host as Administrator:

{% hint style="warning" %}
The following WinRM configuration enables unencrypted traffic and Basic authentication, which transmit credentials in plaintext. Use this configuration for testing and development only. For production environments, configure WinRM over HTTPS (port 5986) with SSL enabled and avoid `AllowUnencrypted` and Basic auth.
{% endhint %}

```powershell
Install-WindowsFeature -name Web-Server -IncludeManagementTools
Install-WindowsFeature -name Web-Asp-Net45
winrm quickconfig -q
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'
```

If the host is an AWS EC2 instance, add an inbound rule to the security group allowing TCP port **5985** from the Delegate's IP or subnet.

When using the PDC infrastructure type, select **Specify hosts** and enter the public IP (or private IP if the Delegate is in the same network) of your Windows Server. For **Specify Credentials**, create a new WinRM credential with these settings:

| Field           | Value                                                                                     |
| --------------- | ----------------------------------------------------------------------------------------- |
| **Auth Scheme** | NTLM                                                                                      |
| **Domain**      | The machine name of the Windows host (for local accounts) or your Active Directory domain |
| **Username**    | `Administrator` (or a user with admin privileges)                                         |
| **Password**    | Create a secret with the host's admin password                                            |
| **Use SSL**     | Unchecked for HTTP (port 5985), checked for HTTPS (port 5986)                             |
| **WinRM Port**  | `5985` (HTTP) or `5986` (HTTPS)                                                           |

Click **Save and Continue**, enter the host IP on the verification screen, and click **Test Connection**. After verification, click **Preview Hosts** to confirm the host resolves correctly.

<details>

<summary>Infrastructure definition YAML example</summary>

The following YAML shows a PDC infrastructure definition for an IIS WinRM deployment. Replace the placeholder values with your own host IP and credential reference.

```yaml
infrastructureDefinition:
  name: winrm
  identifier: winrm
  orgIdentifier: default
  projectIdentifier: YOUR_PROJECT
  environmentRef: YOUR_ENVIRONMENT
  deploymentType: WinRm
  type: Pdc
  spec:
    credentialsRef: YOUR_WINRM_CREDENTIAL
    hostFilter:
      type: All
      spec: {}
    hosts:
      - YOUR_HOST_IP
  allowSimultaneousDeployments: false
```

| Field                          | Description                                                                                 |
| ------------------------------ | ------------------------------------------------------------------------------------------- |
| `deploymentType`               | Must be `WinRm`.                                                                            |
| `type`                         | Infrastructure type. `Pdc` for Physical Data Center.                                        |
| `credentialsRef`               | Reference to the WinRM credential secret you created.                                       |
| `hosts`                        | List of target host IPs or hostnames.                                                       |
| `hostFilter.type`              | Set to `All` to deploy to all specified hosts.                                              |
| `allowSimultaneousDeployments` | Set to `true` if you want multiple pipelines to deploy to this infrastructure concurrently. |

</details>

#### Select the execution strategy and artifact type <a href="#select-the-execution-strategy-and-artifact-type" id="select-the-execution-strategy-and-artifact-type"></a>

The execution strategy screen is where you configure the IIS-specific deployment behavior.

1. In **Execution Strategies**, select **Rolling**, **Canary**, or **Basic** depending on your rollout needs.
2. In the **Artifact Type** dropdown, select the IIS deployment type that matches your use case (**IIS Application**, **IIS Virtual Directory**, **IIS Website**, or **Other**).
3. For **Instances**, configure the instance count or percentage. With Rolling deployments, this controls how many hosts are updated simultaneously.
4. Click **Use Strategy**. Harness adds the **Deploy** step to your execution with the appropriate IIS PowerShell command template.

#### IIS command script templates <a href="#iis-command-script-templates" id="iis-command-script-templates"></a>

When you select an IIS artifact type, Harness automatically populates the Deploy step with a built-in PowerShell command template. For example, selecting **IIS Application** loads the **Default IIS Application PowerShell** template (referenced as `account.Default_IIS_Application_PowerShell`). This template handles downloading the artifact, configuring the IIS application pool, and deploying the application to the target directory.

Click on the **Deploy** step and select the **Inputs** tab to view and customize the template variables. The following table describes each input variable:

| Variable               | Description                                                                                                                          | Default Value                               |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- |
| `ReleaseNo`            | Version identifier for the release. Used to organize deployment directories.                                                         | `0.1`                                       |
| `AppPoolName`          | Name of the IIS Application Pool. Harness creates the pool if it does not exist, or updates it if it does.                           | `DefaultAppPool`                            |
| `AppPoolDotNetVersion` | The .NET CLR version assigned to the application pool (for example, `v4.0` or `v2.0`). Set to an empty string for "No Managed Code". | `v4.0`                                      |
| `DownloadDirectory`    | Local path on the target host where the artifact is downloaded before extraction.                                                    | `$env:TEMP`                                 |
| `AppPhysicalDirectory` | Base path for the application's physical directory on the target host.                                                               | `$env:SYSTEMDRIVE`                          |
| `DestinationDirectory` | Final directory where the application files are deployed. Supports Harness expressions for dynamic paths.                            | `%USERPROFILE%\<+service.name>\<+env.name>` |

You can override any of these values with fixed strings or [Harness expressions](/harness-ai/use-harness-platform/variables-and-expressions/harness-variables.md). After making changes, click **Apply Changes** to save the updated configuration.

{% hint style="info" %}
Use Harness expressions like `<+service.name>` and `<+env.name>` in directory paths to keep deployments organized across services and environments without hardcoding values.
{% endhint %}

<details>

<summary>Pipeline YAML example (Rolling IIS Application)</summary>

The following YAML shows a complete pipeline for a Rolling IIS Application deployment using WinRM. It includes both the deploy and rollback step groups. Replace the placeholder values with your own project, service, environment, and infrastructure references.

```yaml
pipeline:
  name: iis-winrm
  identifier: iiswinrm
  projectIdentifier: YOUR_PROJECT
  orgIdentifier: default
  tags: {}
  stages:
    - stage:
        name: deploy-iis
        identifier: deploy_iis
        description: ""
        type: Deployment
        spec:
          deploymentType: WinRm
          service:
            serviceRef: YOUR_SERVICE
            serviceInputs:
              serviceDefinition:
                type: WinRm
                spec:
                  artifacts:
                    primary:
                      primaryArtifactRef: <+input>
                      sources: <+input>
          environment:
            environmentRef: YOUR_ENVIRONMENT
            deployToAll: false
            infrastructureDefinitions:
              - identifier: YOUR_INFRASTRUCTURE
          execution:
            steps:
              - stepGroup:
                  name: Phase
                  identifier: Phase
                  strategy:
                    repeat:
                      items: <+stage.output.hosts>
                      maxConcurrency: 1
                      partitionSize: 1
                      unit: Count
                  steps:
                    - stepGroup:
                        name: Phase Group
                        identifier: phase_group
                        strategy:
                          repeat:
                            items: <+repeat.partition>
                        steps:
                          - step:
                              name: Deploy
                              identifier: Deploy
                              timeout: 10m
                              template:
                                templateRef: account.Default_IIS_Application_PowerShell
                                templateInputs:
                                  type: Command
                                  spec:
                                    environmentVariables:
                                      - name: ReleaseNo
                                        type: String
                                        value: "0.1"
                                      - name: AppPoolName
                                        type: String
                                        value: DefaultAppPool
                                      - name: AppPoolDotNetVersion
                                        type: String
                                        value: v4.0
                                      - name: DownloadDirectory
                                        type: String
                                        value: $env:TEMP
                                      - name: AppPhysicalDirectory
                                        type: String
                                        value: $env:SYSTEMDRIVE
                                      - name: DestinationDirectory
                                        type: String
                                        value: "%USERPROFILE%\\<+service.name>\\<+env.name>"
            rollbackSteps:
              - stepGroup:
                  name: Phase
                  identifier: Phase
                  strategy:
                    repeat:
                      items: <+stage.output.hosts>
                      maxConcurrency: 1
                      partitionSize: 1
                      unit: Count
                  steps:
                    - stepGroup:
                        name: Phase Group Rollback
                        identifier: phase_group
                        strategy:
                          repeat:
                            items: <+repeat.partition>
                        steps:
                          - step:
                              name: Rollback
                              identifier: Deploy
                              timeout: 10m
                              template:
                                templateRef: account.Default_IIS_Application_PowerShell
                                templateInputs:
                                  type: Command
                                  spec:
                                    environmentVariables:
                                      - name: ReleaseNo
                                        type: String
                                        value: "0.1"
                                      - name: AppPoolName
                                        type: String
                                        value: DefaultAppPool
                                      - name: AppPoolDotNetVersion
                                        type: String
                                        value: v4.0
                                      - name: DownloadDirectory
                                        type: String
                                        value: $env:TEMP
                                      - name: AppPhysicalDirectory
                                        type: String
                                        value: $env:SYSTEMDRIVE
                                      - name: DestinationDirectory
                                        type: String
                                        value: "%USERPROFILE%\\<+service.name>\\<+env.name>"
        tags: {}
        failureStrategies:
          - onFailure:
              errors:
                - AllErrors
              action:
                type: StageRollback
```

Key things to note in this pipeline YAML:

* **`templateRef: account.Default_IIS_Application_PowerShell`**: References the built-in IIS Application template at the account level. Harness provides this template out of the box.
* **Rolling strategy**: The outer step group repeats over `<+stage.output.hosts>` with `maxConcurrency: 1` and `partitionSize: 1`, deploying to one host at a time.
* **Rollback steps**: Mirror the deploy steps so that a failed deployment automatically rolls back using the same IIS template.
* **Environment variables**: These map directly to the IIS command script template inputs described in the table above.

</details>

#### IIS Virtual Directory and IIS Website templates <a href="#iis-virtual-directory-and-iis-website-templates" id="iis-virtual-directory-and-iis-website-templates"></a>

The **IIS Virtual Directory** and **IIS Website** artifact types follow the same pattern as IIS Application but use different PowerShell templates tailored to their specific IIS resource types.

**IIS Virtual Directory** creates or updates a virtual directory under an existing IIS Website. Its template variables are similar to IIS Application but focus on the virtual directory path and physical path mapping rather than application pool settings.

**IIS Website** provisions a complete IIS Website with its own site bindings (protocol, port, hostname) and a dedicated application pool. The template variables include additional fields for configuring site bindings alongside the standard directory and application pool parameters.

Select the appropriate artifact type based on your deployment target, and review the template inputs to confirm they match your IIS configuration.

***

### Run the pipeline to deploy and review <a href="#run-the-pipeline-to-deploy-and-review" id="run-the-pipeline-to-deploy-and-review"></a>

After you select the execution strategy, you are ready to run the pipeline.

Perform the following steps to run the pipeline:

1. Click **Run** to run the pipeline.
2. In **Run Pipeline**, for **Artifact Path**, click the down-drop arrow. Harness displays a list of available artifact packages.
3. Select the artifact file (for example, **todolist.zip** for a generic deployment or your IIS application `.zip` for an IIS deployment).
4. Click **Run Pipeline**. Harness runs the pipeline and the **Console View** displays the tasks executed for each step.

For IIS deployments, the Deploy step executes the IIS PowerShell template which downloads the artifact, creates or updates the application pool, extracts the files to the destination directory, and configures the IIS Application, Virtual Directory, or Website depending on the artifact type you selected. You can verify the deployment by opening the IIS site URL in a browser on the target host.

You have now created and completed the steps for running a pipeline using WinRM.

***

### WinRM session reuse <a href="#winrm-session-reuse" id="winrm-session-reuse"></a>

{% hint style="info" %}
This feature requires:

* Feature flag `CDS_SHARED_SESSION_WINRM_NTLM_NG` enabled for your account
* Minimum delegate version: 889xx

Contact [Harness Support](mailto:support@harness.io) to enable the feature flag.
{% endhint %}

By default, Harness creates a new WinRM connection for every command step in a pipeline. In environments that use JEA or have high session initialization overhead, each new session can take 30 seconds to over a minute to establish. Pipelines with multiple steps accumulate this connection cost on every execution.

With session reuse enabled, Harness maintains a session pool on the delegate. When a command step runs, Harness checks the pool for an existing idle session to the same host using the same WinRM credential. If one is available, it is reused, eliminating the connection setup cost entirely. The pipeline logs show `Reusing shared WinRM session` when a pooled session is used and `Creating new shared WinRM session` when a fresh one is established.

#### Supported authentication methods <a href="#supported-authentication-methods" id="supported-authentication-methods"></a>

Session reuse is supported only for **NTLM authentication**. Kerberos authentication does not support session pooling because it relies on an external process-based authentication model that cannot be effectively pooled.

#### How sessions are pooled <a href="#how-sessions-are-pooled" id="how-sessions-are-pooled"></a>

Sessions in the pool are organized into **session groups**. The WinRM credential (host + username) acts as the key for each group, ensuring that sessions are never shared across different credentials or hosts. Each group can hold multiple idle sessions, and any idle session within the group can be picked up by any command step that uses the same credential.

When the pool reaches its maximum capacity, Harness automatically evicts the oldest idle session to make room for new connections. If all sessions in the pool are currently in use, the new session is created but not added to the pool. After the command completes, the session is closed immediately.

A background cleanup thread runs periodically to remove stale or idle sessions that have exceeded the configured idle timeout. When the delegate shuts down, all pooled sessions are closed automatically through a shutdown hook. This cleanup happens without manual intervention.

**Error handling**

When a session encounters an error during command execution, Harness determines whether to keep or remove the session from the pool based on the error type:

* **Connection errors** (network failures, SSL errors, connection resets, socket errors): The session is immediately removed from the pool and closed because the connection is no longer valid.
* **Command execution errors** (PowerShell script failures, non-zero exit codes): The session remains in the pool for reuse because the underlying connection is still valid. Only the command failed, not the session itself.

#### Session sharing scope <a href="#session-sharing-scope" id="session-sharing-scope"></a>

Sessions in the pool are shared delegate-wide based on credentials:

* Sessions are reused across **all command steps** that use the same host and WinRM credential.
* Sessions can be shared between **different pipeline executions** running concurrently on the same delegate.
* Each command step acquires a session from the pool, executes its commands, and releases the session back to the pool for the next step to use.
* Sessions are never shared **simultaneously**. Only one command executes per session at a time to ensure isolation.

This delegate-wide pooling reduces connection overhead in high-throughput scenarios where multiple pipelines deploy to the same Windows servers.

#### Configure the session pool <a href="#configure-the-session-pool" id="configure-the-session-pool"></a>

You can tune the session pool behavior using the following delegate environment variables:

| Environment variable                          | Default        | Description                                                                                                                                                                   |
| --------------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WINRM_SESSION_POOL_MAX_SESSIONS`             | `100`          | Maximum number of sessions held in the pool at one time. When the pool reaches capacity, the oldest idle session is evicted to make room for new connections.                 |
| `WINRM_SESSION_POOL_IDLE_TIMEOUT_SECONDS`     | `600` (10 min) | How long an idle session can remain in the pool before the cleanup thread removes it. Sessions that exceed this timeout are closed and removed during the next cleanup cycle. |
| `WINRM_SESSION_POOL_CLEANUP_INTERVAL_SECONDS` | `300` (5 min)  | How frequently the cleanup thread scans the pool and removes sessions that have exceeded the idle timeout.                                                                    |

Set these as environment variables on your Harness Delegate. Changes take effect after the delegate restarts.

#### Read the session pool logs <a href="#read-the-session-pool-logs" id="read-the-session-pool-logs"></a>

Each log line from a pooled session includes the group ID and session ID in the format:

```
[<host>:<port>/<user>#<groupId>-<sessionId>]
```

For example:

```
[ec2-54-225-189-86.compute-1.amazonaws.com:5985/Administrator#303541-d9566f]
```

In this example:

* **Group ID** (`303541`): Identifies the session group. All sessions with the same group ID share the same host and credential.
* **Session ID** (`d9566f`): Unique identifier for this specific session instance. Use this to track individual session lifecycle events across multiple command steps.

These identifiers help you correlate log lines across steps and trace which session was used for each command execution.

#### Monitor the session pool <a href="#monitor-the-session-pool" id="monitor-the-session-pool"></a>

The delegate logs pool statistics during each cleanup cycle. Look for log lines like:

```
SharedWinRM session pool statistics: groups=3, totalSessions=12, inUse=4, idle=8, stale=0
```

These metrics help you:

* **Verify sessions are being reused**: Check that `totalSessions` is greater than zero and `inUse` increases during deployments.
* **Detect pool capacity issues**: If `totalSessions` consistently equals `WINRM_SESSION_POOL_MAX_SESSIONS`, consider increasing the pool size.
* **Identify cleanup problems**: If `stale` sessions accumulate, review your idle timeout settings or check for connection issues preventing cleanup.

#### Troubleshooting <a href="#troubleshooting" id="troubleshooting"></a>

The following entries cover common WinRM session reuse issues and how to resolve them.

<details>

<summary>WinRM sessions are not being reused and Harness keeps creating new shared sessions on the delegate</summary>

Confirm the CDS\_SHARED\_SESSION\_WINRM\_NTLM\_NG feature flag is enabled, verify you use NTLM authentication (Kerberos is not pooled), ensure the host and credential are identical so sessions share the same group, and review delegate logs for POOL\_HIT and POOL\_MISS messages (visible only with debug logging enabled).

</details>

<details>

<summary>Harness reports WinRM session pool capacity warnings because the session pool is full</summary>

Increase WINRM\_SESSION\_POOL\_MAX\_SESSIONS, reduce WINRM\_SESSION\_POOL\_IDLE\_TIMEOUT\_SECONDS (kept higher than WINRM\_SESSION\_POOL\_CLEANUP\_INTERVAL\_SECONDS) to free capacity faster, and resolve any persistent connection errors that prevent sessions from returning to the pool.

</details>

<details>

<summary>Increased WinRM connection errors occur after enabling session reuse on the Harness delegate</summary>

Increase the MaxShellsPerUser limit on target hosts, allow persistent WinRM connections through your firewall, and decrease WINRM\_SESSION\_POOL\_IDLE\_TIMEOUT\_SECONDS if the network terminates idle connections prematurely.

</details>

***

### Selective rerun and skipping hosts with the same artifact <a href="#selective-rerun-and-skipping-hosts-with-the-same-artifact" id="selective-rerun-and-skipping-hosts-with-the-same-artifact"></a>

You can do a **selective rerun** for traditional deployments. These improvements ensure:

* **Efficient reruns**: Redeploy only on failed hosts instead of all hosts.
* **Expressions for failed hosts**: Retrieve failed hosts dynamically for debugging and retry logic.

Go to [Selective rerun and skipping hosts with the same artifact](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/traditional/ssh-ng.md#selective-rerun-and-skipping-hosts-with-same-artifact) to configure selective reruns.

***

### Reference hosts in steps using expressions <a href="#reference-hosts-in-steps-using-expressions" id="reference-hosts-in-steps-using-expressions"></a>

You can use the following [instance expressions](/harness-ai/use-harness-platform/variables-and-expressions/harness-variables.md) to reference your hosts:

**Microsoft Azure or Physical Data Center (PDC)**

* `<+instance.hostName>`
* `<+instance.host.hostName>`
* `<+instance.name>`

**Microsoft Azure and AWS**

* `<+instance.host.privateIp>`
* `<+instance.host.publicIp>`

{% hint style="info" %}

* **For AWS with WinRM deployments**: Hostname-based expressions like `<+instance.hostName>` and `<+instance.host.hostName>` are **not supported**. Use `<+instance.host.privateIp>` or `<+instance.host.publicIp>` instead.
* **For Azure with WinRM deployments**: Hostname-based expressions are supported if the hostname is available from the VM metadata.
* `instance.name` has the same value as `instance.hostName`. Both are available for backward compatibility.
  {% endhint %}

***

### Limitations <a href="#limitations" id="limitations"></a>

The **Copy** command for artifacts is not supported in WinRM deployments. As an alternative, you can use the **Download Artifact** command.

***

### FAQs <a href="#faqs" id="faqs"></a>

Go to [SSH and WinRM deployment FAQs](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/traditional/ssh-winrm-faqs.md) to review frequently asked questions about traditional deployments in Harness.

***

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

You have deployed an artifact to a Windows host using WinRM. Continue your learning journey with the following:

* [Provisioning overview](/continuous-delivery/use-continuous-delivery/provision-infrastructure/provisioning-overview.md): Dynamically provision your target infrastructure during deployment.
* [Harness variables and expressions](/harness-ai/use-harness-platform/variables-and-expressions/harness-variables.md): Reference hosts and instance properties in your steps.
