Introduction

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 1 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 127.0.0.1, the command to run is:

New-IscsiTargetPortal -TargetPortalAddress "127.0.0.1" 1 New - IscsiTargetPortal - TargetPortalAddress "127.0.0.1"

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 1 iscsicli listinitiators

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

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

New-IscsiTargetPortal -TargetPortalAddress "127.0.0.1" -InitiatorInstanceName "ROOT\ISCSIPRT\0000_0" 1 New - IscsiTargetPortal - TargetPortalAddress "127.0.0.1" - 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 10.10.10.10 needs to be discovered from IP 10.10.10.11, 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 "10.10.10.10" -InitiatorPortalAddress "10.10.10.11" -InitiatorInstanceName "ROOT\ISCSIPRT\0000_0" 1 New - IscsiTargetPortal - TargetPortalAddress "10.10.10.10" - InitiatorPortalAddress "10.10.10.11" - 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 1 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 1 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 1 Set - MSDSMGlobalDefaultLoadBalancePolicy - Policy RR

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

VSAN from StarWind eliminates any need for physical shared storage just by mirroring internal flash and storage resources between hypervisor servers. Furthermore, the solution can be run on the off-the-shelf hardware. Such design allows VSAN from StarWind to not only achieve high performance and efficient hardware utilization but also reduce operational and capital expenses. Learn more about ➡ VSAN from StarWind .

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 = "127.0.0.1" $node1iscsi1 = "172.16.10.1" $node1iscsi2 = "172.16.11.1" $node2iscsi1 = "172.16.10.2" $node2iscsi2 = "172.16.11.2" $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() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 # StarWind iSCSI connections automation script # Developed by Boris Yurchenko, StarWind # some variables declarations $ initiator = "ROOT\ISCSIPRT\0000_0" $ loopbackip = "127.0.0.1" $ node1iscsi1 = "172.16.10.1" $ node1iscsi2 = "172.16.11.1" $ node2iscsi1 = "172.16.10.2" $ node2iscsi2 = "172.16.11.2" $ 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 ( )

Voila!

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.

Conclusion

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.

Views All Time Views All Time 3 Views Today Views Today 7

Appreciate how useful this article was to you?

5 out of 5, based on 1 review 5 out of 5, based on 1 review

Loading... Loading...