Create an Azure Stack HCI cluster using Windows PowerShell

Applies to: Azure Stack HCI, versions 22H2, 21H2, and 20H2

In this article you will learn how to use Windows PowerShell to create an Azure Stack HCI hyperconverged cluster that uses Storage Spaces Direct. If you're rather use the Cluster Creation wizard in Windows Admin Center to create the cluster, see Create the cluster with Windows Admin Center.


If you're doing a single server installation of Azure Stack HCI 21H2, use PowerShell to create the cluster.

You have a choice between two cluster types:

  • Standard cluster with one or two server nodes, all residing in a single site.
  • Stretched cluster with at least four server nodes that span across two sites, with two nodes per site.

For the single server scenario, complete the same instructions below for the one server.


Stretch clusters are not supported in a single server configuration.

In this article, we will create an example cluster named Cluster1 that is composed of four server nodes named Server1, Server2, Server3, and Server4.

For the stretched cluster scenario, we will use ClusterS1 as the name and use the same four server nodes stretched across sites Site1 and Site2.

For more information about stretched clusters, see Stretched clusters overview.

If you're interested in testing Azure Stack HCI, but have limited or no spare hardware, check out the Azure Stack HCI Evaluation Guide, where we'll walk you through experiencing Azure Stack HCI using nested virtualization inside an Azure VM. Or try the Create a VM-based lab for Azure Stack HCI tutorial to create your own private lab environment using nested virtualization on a server of your choice to deploy VMs running Azure Stack HCI for clustering.

Before you begin

Before you begin, make sure you:

Using Windows PowerShell

You can either run PowerShell locally in an RDP session on a host server, or you can run PowerShell remotely from a management computer. This article will cover the remote option.

When running PowerShell from a management computer, include the -Name or -Cluster parameter with the name of the server or cluster you are managing. In addition, you may need to specify the fully qualified domain name (FQDN) when using the -ComputerName parameter for a server node.

You will also need the Remote Server Administration Tools (RSAT) cmdlets and PowerShell modules for Hyper-V and Failover Clustering. If these aren't already available in your PowerShell session on your management computer, you can add them using the following command: Add-WindowsFeature RSAT-Clustering-PowerShell.

Step 1: Provision the servers

First we will connect to each of the servers, join them to a domain (the same domain the management computer is in), and install required roles and features.

Step 1.1: Connect to the servers

To connect to the servers, you must first have network connectivity, be joined to the same domain or a fully trusted domain, and have local administrative permissions to the servers.

Open PowerShell and use either the fully-qualified domain name or the IP address of the server you want to connect to. You'll be prompted for a password after you run the following command on each server.

For this example, we assume that the servers have been named Server1, Server2, Server3, and Server4:

Enter-PSSession -ComputerName "Server1" -Credential "Server1\Administrator"

Here's another example of doing the same thing:

$myServer1 = "Server1"
$user = "$myServer1\Administrator"

Enter-PSSession -ComputerName $myServer1 -Credential $user


When running PowerShell commands from your management PC, you might get an error like WinRM cannot process the request. To fix this, use PowerShell to add each server to the Trusted Hosts list on your management computer. This list supports wildcards, like Server* for example.

Set-Item WSMAN:\Localhost\Client\TrustedHosts -Value Server1 -Force

To view your Trusted Hosts list, type Get-Item WSMAN:\Localhost\Client\TrustedHosts.

To empty the list, type Clear-Item WSMAN:\Localhost\Client\TrustedHost.

Step 1.2: Join the domain and add domain accounts

So far you've connected to each server node with the local administrator account <ServerName>\Administrator.

To proceed, you'll need to join the servers to a domain and use the domain account that is in the local Administrators group on every server.

Use the Enter-PSSession cmdlet to connect to each server and run the following cmdlet, substituting the server name, domain name, and domain credentials:

Add-Computer -NewName "Server1" -DomainName "" -Credential "Contoso\User" -Restart -Force  

If your administrator account isn't a member of the Domain Admins group, add your administrator account to the local Administrators group on each server - or better yet, add the group you use for administrators. You can use the following command to do so:

Add-LocalGroupMember -Group "Administrators" -Member "king@contoso.local"

Step 1.3: Install roles and features

The next step is to install required Windows roles and features on every server for the cluster. Here are the roles to install:

  • BitLocker
  • Data Center Bridging
  • Failover Clustering
  • File Server
  • FS-Data-Deduplication module
  • Hyper-V
  • Hyper-V PowerShell
  • RSAT-AD-Clustering-PowerShell module
  • RSAT-AD-PowerShell module
  • NetworkATC
  • NetworkHUD
  • SMB Bandwidth Limit
  • Storage Replica (for stretched clusters)

Use the following command for each server (if you're connected via Remote Desktop omit the -ComputerName parameter here and in subsequent commands):

Install-WindowsFeature -ComputerName "Server1" -Name "BitLocker", "Data-Center-Bridging", "Failover-Clustering", "FS-FileServer", "FS-Data-Deduplication", "Hyper-V", "Hyper-V-PowerShell", "RSAT-AD-Powershell", "RSAT-Clustering-PowerShell", "NetworkATC", "NetworkHUD", "SMB Bandwidth Limit", "Storage-Replica" -IncludeAllSubFeature -IncludeManagementTools

To run the command on all servers in the cluster at the same time, use the following script, modifying the list of variables at the beginning to fit your environment:

# Fill in these variables with your values
$ServerList = "Server1", "Server2", "Server3", "Server4"
$FeatureList = "BitLocker", "Data-Center-Bridging", "Failover-Clustering", "FS-FileServer", "FS-Data-Deduplication", "Hyper-V", "Hyper-V-PowerShell", "RSAT-AD-Powershell", "RSAT-Clustering-PowerShell", "NetworkATC", "NetworkHUD", "SMB Bandwidth Limit", "Storage-Replica"

# This part runs the Install-WindowsFeature cmdlet on all servers in $ServerList, passing the list of features in $FeatureList.
Invoke-Command ($ServerList) {
    Install-WindowsFeature -Name $Using:Featurelist -IncludeAllSubFeature -IncludeManagementTools

Next, restart all the servers:

$ServerList = "Server1", "Server2", "Server3", "Server4"
Restart-Computer -ComputerName $ServerList -WSManAuthentication Kerberos

Step 2: Prep for cluster setup

Next, verify that your servers are ready for clustering.

As a sanity check first, consider running the following commands to make sure that your servers don't already belong to a cluster:

Use Get-ClusterNode to show all nodes:


Use Get-ClusterResource to show all cluster nodes:


Use Get-ClusterNetwork to show all cluster networks:


Step 2.1: Prepare drives

Before you enable Storage Spaces Direct, ensure your permanent drives are empty. Run the following script to remove any old partitions and other data.


Exclude any removable drives attached to a server node from the script. If you are running this script locally from a server node for example, you don't want to wipe the removable drive you might be using to deploy the cluster.

# Fill in these variables with your values
$ServerList = "Server1", "Server2", "Server3", "Server4"

Invoke-Command ($ServerList) {
    Get-StoragePool | ? IsPrimordial -eq $false | Set-StoragePool -IsReadOnly:$false -ErrorAction SilentlyContinue
    Get-StoragePool | ? IsPrimordial -eq $false | Get-VirtualDisk | Remove-VirtualDisk -Confirm:$false -ErrorAction SilentlyContinue
    Get-StoragePool | ? IsPrimordial -eq $false | Remove-StoragePool -Confirm:$false -ErrorAction SilentlyContinue
    Get-PhysicalDisk | Reset-PhysicalDisk -ErrorAction SilentlyContinue
    Get-Disk | ? Number -ne $null | ? IsBoot -ne $true | ? IsSystem -ne $true | ? PartitionStyle -ne RAW | % {
        $_ | Set-Disk -isoffline:$false
        $_ | Set-Disk -isreadonly:$false
        $_ | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false
        $_ | Set-Disk -isreadonly:$true
        $_ | Set-Disk -isoffline:$true
    Get-Disk | Where Number -Ne $Null | Where IsBoot -Ne $True | Where IsSystem -Ne $True | Where PartitionStyle -Eq RAW | Group -NoElement -Property FriendlyName
} | Sort -Property PsComputerName, Count

Step 2.2: Test cluster configuration

In this step, you'll ensure that the server nodes are configured correctly to create a cluster. The Test-Cluster cmdlet is used to run tests to verify your configuration is suitable to function as a hyperconverged cluster. The example below uses the -Include parameter, with the specific categories of tests specified. This ensures that the correct tests are included in the validation.

Test-Cluster -Node $ServerList -Include "Storage Spaces Direct", "Inventory", "Network", "System Configuration"

Step 3: Create the cluster

You are now ready to create a cluster with the server nodes that you have validated in the preceding steps.

When creating the cluster, you'll get a warning that states - "There were issues while creating the clustered role that may prevent it from starting. For more information, view the report file below." You can safely ignore this warning. It's due to no disks being available for the cluster witness that you will create later.


If the servers are using static IP addresses, modify the following command to reflect the static IP address by adding the following parameter and specifying the IP address: -StaticAddress <X.X.X.X>;.

New-Cluster -Name $ClusterName –Node $ServerList –nostorage

After the cluster is created, it can some take time for the cluster name to be replicated via DNS across your domain, especially if workgroup servers have been newly added to Active Directory. Although the cluster might be displayed in Windows Admin Center, it might not be available to connect to yet.

A good check to ensure all cluster resources are online:

Get-Cluster -Name $ClusterName | Get-ClusterResource

If resolving the cluster isn't successful after some time, in most cases you can connect by using the name of one of the clustered servers instead of the cluster name.

Step 4: Configure host networking

Microsoft recommends using Network ATC to deploy host networking if you're running Azure Stack HCI version 21H2 or newer. Otherwise, see Host network requirements for specific requirements and information.

Network ATC can automate the deployment of your intended networking configuration if you specify one or more intent types for your adapters. For more information on specific intent types, please see: Network Traffic Types.

Step 4.1: Review physical adapters

On one of the cluster nodes, run Get-NetAdapter to review the physical adapters. Ensure that each node in the cluster has the same named physical adapters and that they report status as 'Up'.

Get-NetAdapter -Name pNIC01, pNIC02 -CimSession (Get-ClusterNode).Name | Select Name, PSComputerName

If a physical adapter name varies across nodes in your cluster, you can rename it using Rename-NetAdapter.

Rename-NetAdapter -Name oldName -NewName newName

Step 4.2: Configure an intent

In this example, an intent is created that specifies the compute and storage intent. See Simplify host networking with Network ATC for more intent examples.

Run the following command to add the storage and compute intent types to pNIC01 and pNIC02. Note that we specify the -ClusterName parameter.

Add-NetIntent -Name Cluster_ComputeStorage -Compute -Storage -ClusterName $ClusterName -AdapterName pNIC01, pNIC02

The command should immediately return after some initial verification.

Step 4.3: Validate intent deployment

Run the Get-NetIntent cmdlet to see the cluster intent. If you have more than one intent, you can specify the Name parameter to see details of only a specific intent.

Get-NetIntent -ClusterName $ClusterName

To see the provisioning status of the intent, run the Get-NetIntentStatus command:

Get-NetIntentStatus -ClusterName $ClusterName -Name Cluster_ComputeStorage

Note the status parameter that shows Provisioning, Validating, Success, Failure.

Status should display success in a few minutes. If this doesn't occur or you see a Status parameter failure, check the event viewer for issues.


At this time, Network ATC does not configure IP addresses for any of its managed adapters. Once Get-NetIntentStatus reports status completed, you should add IP addresses to the adapters.

Step 5: Set up sites (stretched cluster)

This task only applies if you are creating a stretched cluster between two sites with at least two servers in each site.


If you have set up Active Directory Sites and Services beforehand, you do not need to create the sites manually as described below.

Step 5.1: Create sites

In the cmdlet below, FaultDomain is simply another name for a site. This example uses "ClusterS1" as the name of the stretched cluster.

New-ClusterFaultDomain -CimSession "ClusterS1" -FaultDomainType Site -Name "Site1"
New-ClusterFaultDomain -CimSession "ClusterS1" -FaultDomainType Site -Name "Site2"

Use the Get-ClusterFaultDomain cmdlet to verify that both sites have been created for the cluster.

Get-ClusterFaultDomain -CimSession "ClusterS1"

Step 5.2: Assign server nodes

Next, we will assign the four server nodes to their respective sites. In the example below, Server1 and Server2 are assigned to Site1, while Server3 and Server4 are assigned to Site2.

Set-ClusterFaultDomain -CimSession "ClusterS1" -Name "Server1", "Server2" -Parent "Site1"
Set-ClusterFaultDomain -CimSession "ClusterS1" -Name "Server3", "Server4" -Parent "Site2"

Using the Get-ClusterFaultDomain cmdlet, verify the nodes are in the correct sites.

Get-ClusterFaultDomain -CimSession "ClusterS1"

Step 5.3: Set a preferred site

You can also define a global preferred site, which means that specified resources and groups must run on the preferred site. This setting can be defined at the site level using the following command:

(Get-Cluster).PreferredSite = "Site1"

Specifying a preferred Site for stretched clusters has the following benefits:

  • Cold start - during a cold start, virtual machines are placed in the preferred site

  • Quorum voting

    • Using a dynamic quorum, weighting is decreased from the passive (replicated) site first to ensure that the preferred site survives if all other things are equal. In addition, server nodes are pruned from the passive site first during regrouping after events such as asymmetric network connectivity failures.

    • During a quorum split of two sites, if the cluster witness cannot be contacted, the preferred site is automatically elected to win. The server nodes in the passive site then drop out of cluster membership. This allows the cluster to survive a simultaneous 50% loss of votes.

The preferred site can also be configured at the cluster role or group level. In this case, a different preferred site can be configured for each virtual machine group. This enables a site to be active and preferred for specific virtual machines.

Step 5.4: Set-up Stretch Clustering with Network ATC

After version 22H2, you can use Network ATC to set up Stretch clustering. Network ATC adds Stretch as an intent type from version 22H2. To deploy an intent with Stretch clustering with Network ATC, run the following command:

Add-NetIntent -Name StretchIntent -Stretch -AdapterName "pNIC01", "pNIC02"

A stretch intent can also be combined with other intents, when deploying with Network ATC.


Based on steps 5.1-5.3 you can add your pre-created sites to your stretch intent deployed with Network ATC. Network ATC will handle this using SiteOverrides. To create a SiteOverride, run:

 $siteOverride = New-NetIntentSiteOverrides

Once you have created a siteOverride, you can set any property for the siteOverride. The list of properties you can set for a particular siteOverride is: Name, StorageVlan, StretchVlan and ManagementVlan. For eg:

$siteoverride.Name = "A"
$siteoverride.StorageVLAN = 711
$siteoverride.StretchVLAN = 25

Finally, run $siteOverride to make sure all your properties are set in the desired manner.

And finally, to add one or more siteOverrides to your intent, run:

Add-NetIntent -Name StretchIntent -Stretch -AdapterName "pNIC01" , "pNIC02" -SiteOverrides $siteoverride

Step 6: Enable Storage Spaces Direct

After creating the cluster, use the Enable-ClusterStorageSpacesDirect cmdlet, which will enable Storage Spaces Direct and do the following automatically:

  • Create a storage pool: Creates a storage pool for the cluster that has a name like "Cluster1 Storage Pool".

  • Create a Cluster Performance History disk: Creates a Cluster Performance History virtual disk in the storage pool.

  • Create data and log volumes: Creates a data volume and a log volume in the storage pool.

  • Configure Storage Spaces Direct caches: If there is more than one media (drive) type available for Storage Spaces Direct, it enables the fastest as cache devices (read and write in most cases).

  • Create tiers: Creates two tiers as default tiers. One is called "Capacity" and the other called "Performance". The cmdlet analyzes the devices and configures each tier with the mix of device types and resiliency.

For the single server scenario, the only FaultDomainAwarenessDefault is PhysicalDisk. Enable-ClusterStorageSpacesDirect cmdlet will detect single server and automatically configure FaultDomainAwarenessDefault as PyhsicalDisk during enablement.

For stretched clusters, the Enable-ClusterStorageSpacesDirect cmdlet will also do the following:

  • Check to see if sites have been set up
  • Determine which nodes are in which sites
  • Determines what storage each node has available
  • Checks to see if the Storage Replica feature is installed on each node
  • Creates a storage pool for each site and identifies it with the name of the site
  • Creates data and log volumes in each storage pool - one per site

The following command enables Storage Spaces Direct on a multi-node cluster. You can also specify a friendly name for a storage pool, as shown here:

Enable-ClusterStorageSpacesDirect -PoolFriendlyName "$ClusterName Storage Pool" -CimSession $ClusterName

Here's an example on a single-node cluster, disabling the storage cache:

Enable-ClusterStorageSpacesDirect -CacheState Disabled

To see the storage pools, use this:

Get-StoragePool -CimSession $session

After you create the cluster

Now that you are done, there are still some important tasks you need to complete:

Next steps