Monday, February 26, 2018

Don’t break your fingers with hundreds of clicks – automate Windows iSCSI connections

Don't break your fingers with hundreds of clicks – automate Windows iSCSI connections
// StarWind Blog

No ratings yet.


Hi everyone,

I believe any person reading this might have had one's hands on connecting iSCSI targets in Microsoft environments. First, you add target discovery portals with about a dozen of button clicks, then you proceed with connecting the targets with even more dozens of clicks. What if I tell you there is another and far simpler way to do so?

Today, I would like to spend some time on automation of iSCSI connections in Windows. As in most cases, this can be easily achieved using PowerShell, the great automation tool that Microsoft offers to all of us.

Automating Windows iSCSI connections

The initial step is making sure that your server has got it's Microsoft iSCSI service running at startup. Run the below command and make it do so:

Set-Service -Name msiscsi -StartupType Automatic

Further on, we need to discover a target portal – at least one, local or remote, from which we need to get the targets connected. If we discover the loopback interface, the command to run is:

New-IscsiTargetPortal -TargetPortalAddress ""

Pretty straightforward, isn't it? Yet, this can be tricky for some setups. The explanation to this is that when discovering the target portal using the above command, the default iSCSI initiator will be used. In case the system has got more than one iSCSI initiator, this can cause you some headache. So, we need to clearly indicate the initiator to be used when discovering the target portal. To see the list of the initiators available, run the following using the elevated command prompt:

iscsicli listinitiators

For this article, I will use the Microsoft iSCSI initiator, which is ROOT\ISCSIPRT\0000_0 in our example:

Listing iSCSI initiators using CMD or PowerShell

Thus, the amended command for discovering target portals will look like below:

New-IscsiTargetPortal -TargetPortalAddress "" -InitiatorInstanceName "ROOT\ISCSIPRT\0000_0"

In case you need to discover an IP address that is different from the loopback interface, let's say IP needs to be discovered from IP, the commands will undergo further changes. Particularly, I would advise adding the InitiatorPortalAddress parameter. This is done to avoid a mess when establishing the iSCSI connections. The indication of this parameter will ensure correct connection has been made in any case.

New-IscsiTargetPortal -TargetPortalAddress "" -InitiatorPortalAddress "" -InitiatorInstanceName "ROOT\ISCSIPRT\0000_0"

After we have discovered the target portals, our next step is connecting the iSCSI targets exposed by them. The simplest way to connect all iSCSI targets from the discovered portals is by shooting the following:

Get-IscsiTarget | Connect-IscsiTarget

This will connect the targets, but be careful – the connections made with this command will not be reestablished upon a reboot. To make sure they will get reconnected after any server maintenance, the –IsPersistent $true parameter should be used.

Get-IscsiTarget | Connect-IscsiTarget –IsPersistent $true

Note: If the targets are not connected, this will connect all of them. If any of them is already connected, an error will be presented by the PowerShell window for that target.

Our setup procedure has become much better now, yet not perfect. If you require multipathing for your targets, the Set-MSDSMGlobalDefaultLoadBalancePolicy cmdlet can be used, which accepts the -Policy parameter for setting the default MPIO policy.

Set-MSDSMGlobalDefaultLoadBalancePolicy -Policy RR

Here, RR stands for Round Robin. For other options, feel free to check this page from Microsoft.

Generally, by now we have covered the basic procedures for discovering iSCSI target portals and connecting the targets presented by them. And here comes a little bonus. As I am working for StarWind as a technical support engineer, from time to time I need to play around with some test setups. Connecting a bunch of iSCSI targets is a tedious thing, I must confess. When you have at least 3 targets (CSV1, CSV2, and Witness) – to be further added as Cluster Shared Volumes and the Quorum respectively – for proper storage load balancing in the Windows Failover Cluster, connecting them correctly does take time. I do not even mention cases when, in addition to the three afore-mentioned targets you have a bunch of other, like CSV3 to CSV(N). In addition to the above, I adore automation and try to do that wherever possible. This is why I have developed a little script that does the magic instead of me and connects targets for StarWind HA devices (replicated between nodes VSAN1 and VSAN2) in the Microsoft iSCSI initiator.

Just a couple of remarks concerning the script:

  • Node names are expected to be ending in either "1" or "2" (e.g. VSAN1 / VSAN2). If not, rename them or adjust the script
  • Network interface(s) intended for iSCSI connections should have "iscsi" as a part of the label
  • Targets shall be named CSV1, CSV2, and Witness and end like "node_number-csv_csvnumber" (e.g. in "vsan1-csv1"). In fact, the digit in the CSV name does not really matter, but the "CSV" part is introduced into the script through a variable, as well as "Witness", so keep this in mind. You can change the hardcoded values as needed.

Little disclaimer:
I do know that hardcoding is a bad thing for the majority of scenarios. Yet, this is an example script to be extended, improved or adjusted by any reader that might come across this blog post. So, I would like to keep some opportunities for you, guys, to fuel your ego by something like "Dude, I can improve this!". 😊

# StarWind iSCSI connections automation script    # Developed by Boris Yurchenko, StarWind    # some variables declarations    $initiator = "ROOT\ISCSIPRT\0000_0"    $loopbackip = ""    $node1iscsi1 = ""    $node1iscsi2 = ""    $node2iscsi1 = ""    $node2iscsi2 = ""    $csvpartialname = "csv"    $witnesspartialname = "witness"    # clear any previous output from the PowerShell window    cls    write-host "----------------------------    This script will add ISCSI discovery portals to Microsoft ISCSI initiator and connect available disks using the settings for the best performance.    ----------------------------"    # getting to know the node index    $computername = (Get-WmiObject -Class Win32_ComputerSystem -Property Name).Name    $hostLength = $computername.Length    $nodeNumber = $computername.Substring($hostlength - 1)    write-host "Node index detected: $nodeNumber" -ForegroundColor Green    # discovering the network interfaces intended for iSCSI connections    $iscsiinterfaces = Get-WmiObject Win32_NetworkAdapter | Select-Object -ExpandProperty NetConnectionID    $iscsiinterfaces = $iscsiinterfaces -like "iscsi*"    $iscsinumber = $iscsiinterfaces.Count    write-host "$iscsinumber iSCSI interfaces discovered"    # turning on autostart for the Microsoft iSCSI service    write-host "Autostarting iSCSI service"    Set-Service -Name msiscsi -StartupType Automatic    write-host "Adding iSCSI discovery portals"    # adding loopback interface and partner node interface(s) to the discovered portals    New-IscsiTargetPortal -TargetPortalAddress $loopbackip -InitiatorInstanceName $initiator | out-null    if ($nodeNumber -eq "1") {    New-IscsiTargetPortal -TargetPortalAddress $node2iscsi1 -InitiatorPortalAddress $node1iscsi1 -InitiatorInstanceName $initiator | out-null    if ($ISCSIinterfaces.Length -eq "2") {    New-IscsiTargetPortal -TargetPortalAddress $node2iscsi2 -InitiatorPortalAddress $node1iscsi2 -InitiatorInstanceName $initiator | out-null    }    } elseif ($nodeNumber -eq "2") {    New-IscsiTargetPortal -TargetPortalAddress $node1iscsi1 -InitiatorPortalAddress $node2iscsi1 -InitiatorInstanceName $initiator | out-null    if ($ISCSIinterfaces.Length -eq "2"){    New-IscsiTargetPortal -TargetPortalAddress $node1iscsi2 -InitiatorPortalAddress $node2iscsi2 -InitiatorInstanceName $initiator | out-null    }    }    write-host "Connecting iSCSI targets from discovered portals"    $loopback = read-host "Number of loopback sessions to add"    $partner = read-host "Number of partner sessions to add (for each interface, if more than one iSCSI interface is available)"    # magic happening further    $localISCSItargets = Get-IscsiTarget    $targetsnumber = $localISCSItargets.Count    write-host "We have $targetsnumber targets to be connected"    for ($i=0; $i -lt $targetsnumber; $i++) {    $activetarget = $localISCSItargets[$i].NodeAddress    if ($nodeNumber -eq "1"){    if ($activetarget -Like "*1-$witnesspartialname*") {    Connect-IscsiTarget -NodeAddress "$activetarget" -TargetPortalAddress $loopbackip -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    if ($activetarget -Like "*1-$csvpartialname*"){    for ($j=0; $j -lt $loopback; $j++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -TargetPortalAddress $loopbackip -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    }    if ($activetarget -Like "*2-$csvpartialname*") {    if ($iscsinumber -eq "1") {    for ($l=0; $l -lt $partner; $l++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node1iscsi1 -TargetPortalAddress $node2iscsi1 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    } elseif ($iscsinumber -eq "2") {    for ($k=0; $k -lt $partner; $k++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node1iscsi1 -TargetPortalAddress $node2iscsi1 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node1iscsi2 -TargetPortalAddress $node2iscsi2 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    }    }    } elseif ($nodeNumber -eq "2") {    if ($activetarget -Like "*2-$witnesspartialname*") {    Connect-IscsiTarget -NodeAddress "$activetarget" -TargetPortalAddress $loopbackip -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    if ($activetarget -Like "*2-$csvpartialname*"){    for ($z=0; $z -lt $loopback; $z++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -TargetPortalAddress $loopbackip -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    }    if (($activetarget -Like "*1-$csvpartialname*")) {    if ($iscsinumber -eq "1") {    for ($x=0; $x -lt $partner; $x++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node2iscsi1 -TargetPortalAddress $node1iscsi1 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    } elseif ($iscsinumber -eq "2") {    for ($y=0; $y -lt $partner; $y++) {    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node2iscsi1 -TargetPortalAddress $node1iscsi1 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    Connect-IscsiTarget -NodeAddress "$activetarget" -InitiatorPortalAddress $node2iscsi2 -TargetPortalAddress $node1iscsi2 -InitiatorInstanceName $initiator -IsMultipathEnabled $true -IsPersistent $true    }    }          }    }    }    # setting MPIO load balance policy    Set-MSDSMGlobalDefaultLoadBalancePolicy -Policy LQD    write-host "    Press any key to exit"    $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null    $HOST.UI.RawUI.Flushinputbuffer()


Another small remark goes here. If you decide to assign a separate MPIO load balance policy for each device, unfortunately, it seems to be impossible with PowerShell, as MPIO policy is applied to all of them at the same time when done through PowerShell. So, guys, if any of you knows how to define a separate MPIO load balance policy for certain devices without affecting other ones, feel free to share this with me and the community in the comments below this post. I will really appreciate it, as this thing has been annoying me for quite a period of time already.


If you have a single environment with only several iSCSI targets discovered from a couple of target portals, messing with automation may not be worth it. Yet, if you have multiple environments with a bunch of portals and targets that need to be discovered and connected, and all of them are more or less similar in terms of configuration, you might find your resort in automating the whole process.

I hope to post some other automation things here, so tune in and check the StarWind blog from time to time.

Please rate this


Read in my feedly

Sent from my iPhone

No comments:

Post a Comment