Configuring iSCSI and iSNS with DSC

Several months back I created a DSC Resource for configuring iSCSI Server Targets (including Virtual Disks) as well as iSCSI Initiators using Desired State Configuration (DSC). I created this for several reasons:

  1. I needed a way for LabBuilder to automatically build Scale-Out File Servers (with CSVs).
  2. I needed something to use as an example in my Creating Professional DSC Resources series.
  3. No one else had already created one.

This weekend I decided to add iSNS Server support to the resource – for both the ciSCSIServerTarget and ciSCSIInitiator resources. So with that feature added I thought it might be a good opportunity for me to write a quick blog post on how to use these DSC Resources.

Installing the Resource

You can find the new ciSCSI resource in the PowerShell Gallery.

For those of you using Windows Management Framework 5.0 (or have the PowerShellGet module installed) you can just use the command:

Install-Module -Name ciSCSI

If you don’t have Windows Management Framework 5.0 (and don’t have the PowerShellGet module installed) you will need to download and install the resource from the GitHub Repository.

Using the Resource

If you’d rather just jump right into the resource documentation and examples you can find it here. Otherwise, read on and I’ll cover this resource to configure both an iSCSI Server Target and an iSCSI Initiator. I’ll also show how to register iSCSI Server Targets and Initiators with an iSNS Server.

Important: Although the ciSCSI DSC Resource will work on Windows Management Framework 4.0, these examples require the use of the WaitForAny DSC Resource, which is only available in Windows Management Framework 5.0. This resource is used to ensure that the iSCSI Server Target has been created before trying to connect any iSCSI Initiators to it. The resource could be omitted, but errors will reported by the LCM on the iSCSI Initiator computers if the iSCSI Server Target is not available before the iSCSI Initiator DSC MOF is applied.

The Example Environment

In this example, the DSC Configurations that are being created will refer to the following servers:

  • FS1.CONTOSO.COM – this is the file server that will contain the iSCSI Virtual Disks and iSCSI Server Target.
  • CLUS1.CONTOSO.COM,CLUS2.CONTOSO.COM,CLUS3.CONTOSO.COM – these are the Windows Server 2012 R2 (or Windows Server 2016) Cluster Server nodes that will be connecting to the iSCSI Server Target.
  • ISNS1.CONTOSO.COM – this is a server with the iSNS Server Windows Feature installed on it. The iSNS default domain has been configured on this server already.

The DSC configurations that will be created will create four 128GB dynamic iSCSI Virtual Disks on the D:\ drive of FS1.CONTOSO.COM. An iSCSI Server Target called FS1-Server-Target will be created and the four iSCSI Virtual Disks attached to it.

Configuring the iSCSI Server Target

A DSC configuration that creates an iSCSI Server Target requires the following steps to be performed in the DSC Resource:

  1. Install the iSCSI Target Server Windows Feature (FS-iSCSITarget-Server).
  2. Initialize and physical disks that will be used to store the iSCSI Virtual Disks (optional).
  3. Create the iSCSI Virtual Disks that will be used by the iSCSI Server Target.
  4. Create the iSCSI Server Target and optionally register it with an iSNS Server.

Here is the DSC Configuration:

Configuration ISCSI_SERVER_TARGET
{
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
Import-DscResource -ModuleName xStorage
Import-DscResource -ModuleName ciSCSI
Node 'FS1' {
# Step 1 - Install the iSCSI Target Server Windows Feature (FS-iSCSITarget-Server).
WindowsFeature ISCSITargetServerInstall
{
Ensure = "Present"
Name = "FS-iSCSITarget-Server"
}
# Step 2 - Initialize and physical disks that will be used to store the iSCSI Virtual Disks (optional).
xWaitforDisk Disk2
{
DiskNumber = 1
RetryIntervalSec = 60
RetryCount = 60
}
xDisk DVolume
{
DiskNumber = 1
DriveLetter = 'D'
DependsOn = "[xWaitforDisk]Disk2"
}
File VirtualDisksFolder
{
Ensure = 'Present'
DestinationPath = 'D:\iSCSIVirtualDisks'
Type = 'Directory'
DependsOn = '[xDisk]DVolume'
}
# Step 3 - Create the iSCSI Virtual Disks that will be used by the iSCSI Server Target
ciSCSIVirtualDisk VDisk1
{
Ensure = 'Present'
Path = 'D:\iSCSIVirtualDisks\VDisk1.vhdx'
DiskType = 'Dynamic'
SizeBytes = 128GB
DependsOn = "[File]VirtualDisksFolder"
}
ciSCSIVirtualDisk VDisk2
{
Ensure = 'Present'
Path = 'D:\iSCSIVirtualDisks\VDisk2.vhdx'
DiskType = 'Dynamic'
SizeBytes = 128GB
DependsOn = "[File]VirtualDisksFolder"
}
ciSCSIVirtualDisk VDisk3
{
Ensure = 'Present'
Path = 'D:\iSCSIVirtualDisks\VDisk3.vhdx'
DiskType = 'Dynamic'
SizeBytes = 128GB
DependsOn = "[File]VirtualDisksFolder"
}
ciSCSIVirtualDisk VDisk4
{
Ensure = 'Present'
Path = 'D:\iSCSIVirtualDisks\VDisk4.vhdx'
DiskType = 'Dynamic'
SizeBytes = 128GB
DependsOn = "[File]VirtualDisksFolder"
}
# Step 4 - Create the iSCSI Server Target and optionally register it with an iSNS Server.
ciSCSIServerTarget iSCSIServerTarget
{
Ensure = 'Present'
TargetName = 'FS1-Server-Target'
InitiatorIds = @(
'Iqn:iqn.1991-05.com.microsoft:CLUS1.CONTOSO.COM'
'Iqn:iqn.1991-05.com.microsoft:CLUS2.CONTOSO.COM'
'Iqn:iqn.1991-05.com.microsoft:CLUS3.CONTOSO.COM'
)
Paths = @(
'D:\iSCSIVirtualDisks\VDisk1.vhdx'
'D:\iSCSIVirtualDisks\VDisk2.vhdx'
'D:\iSCSIVirtualDisks\VDisk3.vhdx'
'D:\iSCSIVirtualDisks\VDisk4.vhdx'
)
iSNSServer = 'isns1.contoso.com'
}
}
}

Important: Note that the TargetName is set to ‘FS1-Server-Target‘, which will automatically configure the Target IQN to ‘iqn.1991-05.com.microsoft:FS1-FS1-Server-Target-Target’. This is because the Microsoft iSCSI Server Target cmdlets automatically name the Server Target for you using the following format:

"iqn.1991-05.com.microsoft:$($ComputerName)-$($ServerTarget)-Target"

This is very important to remember because the iSCSI Initiators use this string to identify the Server Target to connect to.

The rest of the components of this DSC Configuration are self-explanatory as long as you keep in mind the example environment that is being configured.

Configuring the iSCSI Initiator

A DSC configuration for each of the iSCSI Initiators that will connect to the iSCSI Server Target requires the following steps to be performed in the DSC Resource:

  1. Start the Microsoft iSCSI Initiator Service service (MSiSCSI).
  2. Use the WaitForAny WMF 5.0 DSC Resource to wait for the iSCSI Server Target to be created (optional).
  3. Connect the iSCSI Initiator to the iSCSI Server Target and optionally register it with an iSNS Server.

Here is the DSC Configuration for CLUS1.CONTOSO.COM (the configuration for the other nodes would be similar except with different InitiatorPortalAddress values):

Configuration ISCSI_INITIATOR
{
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName xPSDesiredStateConfiguration
Import-DscResource -ModuleName ciSCSI
Node 'CLUS1' {
# Step 1 - Start the Microsoft iSCSI Initiator Service service (MSiSCSI)
Service iSCSIService
{
Name = 'MSiSCSI'
StartupType = 'Automatic'
State = 'Running'
}
# Step 2 - Use the WaitForAny WMF 5.0 DSC Resource to wait for the iSCSI Server Target to be created (optional).
WaitForAny WaitForiSCSIServerTarget
{
ResourceName = "[ciSCSIServerTarget]ClusterServerTarget"
NodeName = 'FS1'
RetryIntervalSec = 30
RetryCount = 30
DependsOn = "[Service]iSCSIService"
}
# Step 3 - Connect the iSCSI Initiator to the iSCSI Server Target and optionally register it with an iSNS Server.
ciSCSIInitiator iSCSIInitiator
{
Ensure = 'Present'
NodeAddress = 'iqn.1991-05.com.microsoft:fs1-fs1-server-target-target'
TargetPortalAddress = '192.168.129.10'
InitiatorPortalAddress = '192.168.129.24'
IsPersistent = $true
iSNSServer = 'isns1.contoso.com'
DependsOn = "[WaitForAny]WaitForiSCSIServerTarget"
} # End of ciSCSITarget Resource
}
}

Important: We need to make sure the NodeAddress is set to the the Target IQN from the iSCSI Server Target – in this case ‘iqn.1991-05.com.microsoft:FS1-FS1-Server-Target-Target’.

It is also recommended that you use IP Addresses for the TargetPortalAddress and InitiatorPortalAddress parameters rather than server names, as this will force the iSCSI traffic to use the appropriate network adapter.

The components of this DSC Configuration are self-explanatory as long as you keep in mind the example environment that is being configured.

iSNS Server Configuration

There are a few things to keep in mind when you have your iSCSI DSC Configurations registering with an iSNS Server:

  1. The Default Domain on the iSNS Server should have been created.
  2. If the iSNS Server is not available or contactable by the iSCSI Server Target or Initiator when the DSC Configuration is applied the DSC configuration will not throw an error, but the iSNS Server Address will not be set. However, next time the DSC configuration is applied by the LCM it will try again (and again the next time etc).

Using iSNS Server is completely optional and is mostly used in larger environments with more than twenty iSCSI Server Targets and where the Initiators will be connected to the iSCSI Server Targets manually or where DSC can’t be used on the iSCSI Server Targets.

That is all there is to using this resource to configure a Windows Server 2012 iSCSI SAN using DSC.

Note: I have submitted this DSC Resource to be included in the Microsoft Community DSC Resources project. If it is accepted then the name of the DSC Resource will change from ciSCSI to iSCSI. The resource hasn’t yet been reviewed and I’m not aware of an ETA for it. The old ‘c’ and ‘x’ nomenclature used by DSC Resources is being phased out.

If you need some additional guidance or other specific examples, please feel free to drop a comment on this blog post (or the GitHub repository) and I’ll do my best to help you out.

 

 

Configure iSNS Server in an iSCSI Initiator with PowerShell

This will just be a very quick post today.

Configuring an iSCSI Initiator to use an iSNS Server is fairly easy using the iSCSI configuration utility:

ss_isns_guiinitiatorconfig

But lets face it, that’s no fun and it really doesn’t fit well when when we have to configure 10’s or 100’s of initiators or we’re using Server Core. Not to mention that this is something we’d really want to do with Desired State Configuration. Besides, this is a PowerShell blog.

It is easy enough to configure iSCSI Targets to register with an iSNS Server:

Set-WmiInstance -Namespace root\wmi -Class WT_iSNSServer –Arguments @{ServerName="ISNSSERVER.CONTOSO.COM"}

But unfortunately I couldn’t find any documentation on how to do this on an iSCSI Initiator. But after a little bit of digging around WMI I found the appropriate class:

MSiSCSIInitiator_iSNSServerClass

So, to add the iSNS Server to the iSCSI Initiator:

Set-WmiInstance `
-Namespace root\wmi `
-Class MSiSCSIInitiator_iSNSServerClass `
-Arguments @{iSNSServerAddress="ISNSSERVER.CONTOSO.COM"}

Notice that the WMI Class argument name for the setting the iSNS Server name in an iSCSI Initiator is different (iSNSServerAddress) compared to setting it for an iSCSI Target (ServerName).

To list the currently set iSNS Servers:

Get-WmiObject `
-Class MSiSCSIInitiator_iSNSServerClass `
-Namespace root\wmi

ss_isns_getlistservers

And if you need to remove an iSNS Server from the iSCSI Initiator:

Get-WmiObject `
-Class MSiSCSIInitiator_iSNSServerClass `
-Namespace root\wmi `
-Filter "iSNSServerAddress='ISNSSERVER.CONTOSO.COM'" | Remove-WmiObject -Verbose

Pretty easy right?

In a few weeks I plan to integrate iSNS registration with my iSCSI DSC Resources (also available on PowerShell Gallery) so that the whole process of registering iSCSI Initiators and Targets with iSNS is just a little bit easier.

Thanks for reading.

Speed up iSCSI PowerShell cmdlets

I’ve been spending a bit of time lately completing a set of DSC Resources for configuring iSCSI Targets and iSCSI Initiators. One thing I’ve noticed is that these iSCSI cmdlets are extremely slow:

ss_iscsi_measuregetiscsivirtualdisk

21 seconds is just too slow!

Now, if I was just calling this cmdlet every now and then it really wouldn’t matter so much – as long as it works. However, because this cmdlet is going to be called every few minutes when used in a DSC Resource it is unacceptable.

I’ve seen this sort of issue before. It is often caused by the cmdlet looking for things on the network that don’t exist. This means that a TCP timeout might occur before the cmdlet will complete. So I thought a way of possibly eliminating this is to specify the computer the cmdlet should work against – in the case of a DSC Resource it is always LocalHost.

So I gave this a shot:

ss_iscsi_measuregetiscsivirtualdisklocalhost

49 milliseconds is very acceptable.

That is a 42,000% speed increase, which is definitely acceptable.

To confirm that the cmdlet is still working as expected:

ss_iscsi_getiscsivirtualdisklocalhost

Yes, all the iSCSI Virtual Disks are there.

Important Note: You must set the ComputerName to the exact test “LocalHost” and not the actual computer name of the local machine. If you use the local computer name, the cmdlet will still be slow:

ss_iscsi_measureiscsivirtualdisklocalcomputer

21 seconds again, no good.

All of the iSCSI Target cmdlets in the iSCSI Target module seem to suffer from this problem, so adding the –ComputerName LocalHost parameter to them should speed things up across the board. Obviously, this is only going to work if you’re actually manipulating iSCSI Targets on the LocalHost – if you’re trying to configure a remote computer then you’ll need to set the remote computer name.

Hope this one shaves a number of seconds off some scripts out there.

Also, Happy 2016!

Remove an iSCSI Target Portal with PowerShell

I ran into a small problem with removing iSCSI Target Portals using PowerShell the other day and thought it might be worth documenting.

Pretend you have an iSCSI Target Portal configured with a Target Portal Address of 192.168.129.24:

ss_iscsi_gettargetportal

You might therefore expect that you could remove this Target Portal with the command:

Remove-IscsiTargetPortal -TargetPortalAddress 192.168.129.24

Unfortunately this won’t work:

ss_iscsi_removetargetportal1

And neither does this:

ss_iscsi_removetargetportal2

What you actually have to do is specify both the Target Portal Address and the Initiator Portal Address when deleting an iSCSI Target Portal:

Remove-IscsiTargetPortal -TargetPortalAddress 192.168.129.24 -InitiatorPortalAddress 192.168.129.30

ss_iscsi_removetargetportalcorrect

Over and out.