# encoding: utf-8 # api: powershell # title: iSCSI Module # description: A module that wraps iscsicli.exe to provide basic iSCSI management capabilities # version: 6.1 # type: module # author: beefarino # license: MITL # function: Add-Target # x-poshcode-id: 3067 # x-archived: 2012-02-05T04:37:51 # x-published: 2012-11-21T13:29:00 # # # Copyright (c) 2011 Code Owls LLC, All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or # substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # http://www.opensource.org/licenses/mit-license.php # # PowerShell module for iSCSI management # # author: # jim christopher # # notes: # #Target Mappings: # is the LUN value the target uses to expose the LUN. # It must be in the form 0x0123456789abcdef # is the bus number the OS should use to surface the LUN # is the target number the OS should use to surface the LUN # is the LUN number the OS should use to surface the LUN # #CHAP secrets, CHAP passwords and IPSEC preshared keys can be specified as #a text string or as a sequence of hexadecimal values. The value specified on #the command line is always considered a string unless the first two characters #0x in which case it is considered a hexadecimal value. # #For example 0x12345678 specifies a 4 byte secret # #All numerical values are assumed decimal unless preceeded by 0x. If #preceeded by 0x then value is assumed to be hex # #iscsicli can also be run in command line mode where iscsicli commands #can be entered directly from the console. To enter command line #mode, just run iscsicli without any parameters ########################################################### ## flags and enumerations #Payload Id Type: # ID_IPV4_ADDR is 1 - Id format is 1.2.3.4 # ID_FQDN is 2 - Id format is ComputerName # ID_IPV6_ADDR is 5 - Id form is IPv6 Address # $payloadIPV4 = 1; $payloadFQDN = 2; $payloadIPV6 = 5; #Security Flags: # TunnelMode is 0x00000040 # TransportMode is 0x00000020 # PFS Enabled is 0x00000010 # Aggressive Mode is 0x00000008 # Main mode is 0x00000004 # IPSEC/IKE Enabled is 0x00000002 # Valid Flags is 0x00000001 # $securityTunnelMode = 0x40; $securityTransportMode = 0x20; $securityPFSEnable = 0x10; $securityAggressiveMode = 0x08; $securityMainMode = 0x04; $securityIPSECIKEEnabled = 0x02; $securityValidFlags = 0x01; #Login Flags: # ISCSI_LOGIN_FLAG_REQUIRE_IPSEC 0x00000001 # IPsec is required for the operation # # ISCSI_LOGIN_FLAG_MULTIPATH_ENABLED 0x00000002 # Multipathing is enabled for the target on this initiator # $loginRequireIPSEC = 0x01; $loginMultipathEnabled = 0x02; #AuthType: # ISCSI_NO_AUTH_TYPE = 0, # No iSCSI in-band authentication is used # # ISCSI_CHAP_AUTH_TYPE = 1, # One way CHAP (Target authenticates initiator is used) # # ISCSI_MUTUAL_CHAP_AUTH_TYPE = 2 # Mutual CHAP (Target and Initiator authenticate each other is used) # $authTypeNone = 0; $authTypeChap = 1; $authTypeMutualChap = 2; #Target Flags: # ISCSI_TARGET_FLAG_HIDE_STATIC_TARGET 0x00000002 # If this flag is set then the target will never be reported unless it # is also discovered dynamically. # # ISCSI_TARGET_FLAG_MERGE_TARGET_INFORMATION 0x00000004 # If this flag is set then the target information passed will be # merged with any target information already statically configured for # the target # $targetHideStaticTarget = 0x02; $targetMergeTargetInfo = 0x04; ########################################################### ## cmdlets #iscsicli AddTarget # #
# # # # ... # #iscsicli QAddTarget # function Add-Target { [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)] [Alias("Name")] [string] # the name of the target $targetName, [Parameter(Mandatory=$true)] [Alias("Address")] [string] # the IP or DNS address of the target portal $targetPortalAddress ) process { & iscsicli qaddtarget $targetName, $targetPortalAddress; } } #iscsicli RemoveTarget # #RemovePersistentTarget # # # # function Remove-Target { <# This command will remove a target from the list of persisted targets. Boot Configuration Known Issues (Windows Server 2003 Boot Initiator) The Microsoft iSCSI Software Initiator boot version GUI does not allow you to view which adapter is set to boot. In order to determine which adapter the system is set to boot with, you can use the following command: From a command prompt type �iscsibcg /showibf� to find the MAC address of the boot adapter Then run the command �ipconfig /all� Compare the MAC address of the adapter to those listed with ipconfig /all MPIO Failover in an iSCSI boot configuration using the Microsoft iSCSI Software Initiator In Fail Over Only, no load balancing is performed. The primary path functions as the active path and all other paths are standby paths. The active path is used for sending all I/O. If the active path fails, one of the standby paths becomes the active path. When the formerly active path is reconnected, it becomes a standby path and a "failback" does not occur. This behavior is due to Media Sensing is disabled by default in the boot version of the Microsoft iSCSI Software Initiator and is by design. However, the registry key can be changed to enable fail back. For more information, please see For more information: 239924 How to disable the Media Sensing feature for TCP/IP in Windows http://support.microsoft.com/default.aspx?scid=kb;EN-US;239924 #> [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='high')] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Alias("Name")] [string] # the name of the target to remove $targetName, [Parameter(ValueFromPipelineByPropertyName=$true)] [Alias("Address")] [string] # the IP or DNS address of the target portal $targetPortalAddress, [Parameter(ValueFromPipelineByPropertyName=$true)] [Alias('Port')] [int] #the TCP port number of the target portal. Typically this is 3260, which is the well-known port number defined for use by iSCSI. $TargetPortalSocket = 3260, [Parameter(ValueFromPipelineByPropertyName=$true)] [Alias( "InitiatorInstanceName" )] [string] #the name of the initiator via which the SendTargets operation is performed. If not specified then the initiator used is selected by the iSCSI initiator service. $InitiatorName, [Parameter(ValueFromPipelineByPropertyName=$true)] [string] #is the physical port number on the initiator via which the SendTargets operation is performed. If not specified then the kernel mode initiator driver chooses the initiator port used. $InitiatorPort = '*', [Parameter()] [switch] # specify to remove a persistent connection $persist, [Parameter()] [switch] # specify to bypass standard PowerShell confirmation procedures $force ) process { Write-Verbose "remove-target ..."; Write-Verbose " TargetName: $targetName"; Write-Verbose " TargetPortalAddress: $targetPortalAddress"; Write-Verbose " TargetPortalSocket: $targetPortalSocket"; Write-Verbose " InitiatorInstanceName: $InitiatorName"; Write-Verbose " InitiatorPort: $initiatorPort"; Write-Verbose " Persist: $persist"; Write-Verbose " Force: $force"; if( -not ( $force -or $pscmdlet.ShouldProcess( $targetName, 'Remove iSCSI target' ) ) ) { return; } if( $persist -and $InitiatorName ) { $iscsi = "iscsicli removepersistenttarget $InitiatorName $targetName $InitiatorPort $targetPortalAddress $TargetPortalSocket" Write-Verbose $iscsi; invoke-expression $iscsi } else { $iscsi = "iscsicli removetarget $targetName"; Write-Verbose $iscsi; invoke-expression $iscsi } } } #iscsicli AddTargetPortal # [HBA Name] [Port Number] # #
# # # #iscsicli QAddTargetPortal # [CHAP Username] [CHAP Password] # function Add-TargetPortal { <# This command will add a target portal to the list of persisted target portals. The iSCSI initiator service will perform a SendTargets operation to each target portal in the list whenever the service starts and whenever a full refresh of the target list is requested. #> #[CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)] [Alias("Address")] [string] # the IP or DNS address of the target portal. $targetPortalAddress, [Parameter()] [string] #Username is the string that should be used as the CHAP username when logging into the target. By specifying * for this parameter, the iSCSI initiator service will use the initiator node name as the CHAP username. $username, [Parameter()] [string] #Password is the string that should be used as the target�s CHAP secret when logging into the target. The initiator will use this secret to compute a hash value based on the challenge sent by the target. $password ) process { if( $username ) { & iscsicli qaddtargetportal $targetPortalAddress $username $password; } else { & iscsicli qaddtargetportal $targetPortalAddress; } } } #iscsicli RemoveTargetPortal [HBA Name] [Port Number] # function Remove-TargetPortal { <# This command will remove a target portal from the list of persisted target portals. The iSCSI initiator service will perform a SendTargets operation to each target portal in the list whenever the service starts and whenever a full refresh of the target list is requested. Note that the command does not purge the targets discovered via this target portal from the list of targets maintained by the service. #> [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [Alias('Name')] [string] # the IP or DNS address of the target portal. $targetPortalAddress, [Parameter()] [Alias('Port')] [int] #the TCP port number of the target portal. Typically this is 3260, which is the well-known port number defined for use by iSCSI. $TargetPortalSocket = 3260, [Parameter()] [string] #the name of the initiator via which the SendTargets operation is performed. If not specified then the initiator used is selected by the iSCSI initiator service. $InitiatorName = '', [Parameter()] [string] #is the physical port number on the initiator via which the SendTargets operation is performed. If not specified then the kernel mode initiator driver chooses the initiator port used. $InitiatorPort = '' ) process { & iscsicli removetargetportal $targetPortalAddress $TargetPortalSocket $InitiatorName $InitiatorPort } } #iscsicli RefreshTargetPortal [HBA Name] [Port Number] # function Update-TargetPortal { <# This command will perform a SendTargets operation to the target portal and include the discovered targets into the list of targets maintained by the service. It does not add the target portal to the persistent list. #> [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)] [Alias("Address")] [string] # the IP or DNS address of the target portal. $targetPortalAddress, [Parameter()] [Alias("Port")] [int] #the TCP port number of the target portal. Typically this is 3260, which is the well-known port number defined for use by iSCSI. $TargetPortalSocket = 3260, [Parameter()] [string] #the name of the initiator via which the SendTargets operation is performed. If not specified then the initiator used is selected by the iSCSI initiator service. $InitiatorName, [Parameter()] [int] #is the physical port number on the initiator via which the SendTargets operation is performed. If not specified then the kernel mode initiator driver chooses the initiator port used. $InitiatorPort ) process { & iscsicli refreshtargetportal $targetPortalAddress $TargetPortalSocket $InitiatorName $InitiatorPort } } #iscsicli ListTargets [ForceUpdate] # #iscsicli ListPersistentTargets # function Get-Target { <# This command will display the list of persistent targets configured for all initiators. #> [CmdletBinding(DefaultParameterSetName='Local')] param( [Parameter( ParameterSetName='Persistent' )] [switch] # specify to get persistent targets $persistent, [Parameter( ParameterSetName='Local' )] [switch] # specify to force refresh of target list during retrieval $force ) process { if( $persistent ) { $data = & iscsicli ListPersistentTargets | Out-String; $data | Write-Verbose; $data -replace "[`r`n]+","=" -split "==" | where { $_ -match ':\s+' } | foreach { $_ | convertFrom-iSCSIOutput # Write-Verbose "section $_"; # $a = @{}; # $_ -split '=' | Select-String '^\s+[\S\s]+:\s+' | foreach{ # Write-Verbose "item entry $_"; # $k,$v = $_ -split ':',2 # $a[$k.trim(' ')] = $v.trim(' '); # # #todo - massage to match remove-target inputs # } # new-object psobject -Property $a; } } else { if( $force ) { $data = & iscsicli ListTargets T } else { $data = & iscsicli ListTargets } $data | Select-String '^\s+\S+:\S+$' | foreach{ $_ -replace '^\s+','' -replace '\s+$','' }; } } } #iscsicli ListTargetPortals # function Get-TargetPortal { [CmdletBinding()] param() process { $data = & iscsicli ListTargetPortals | Out-String; $data | Write-Verbose; $data -replace "[`r`n]+","=" -split "==" | where { $_ -match ':\s+' } | foreach { $_ | convertFrom-iSCSIOutput # Write-Debug "section $_"; # $a = @{}; # $_ -split '=' | Select-String '^\s+[\S\s]+:\s+' | foreach{ # Write-Debug "item entry $_"; # $k,$v = $_ -split ':',2 # $a[$k.trim(' ')] = $v.trim(' '); # } # new-object psobject -Property $a; } } } #iscsicli TargetInfo [Discovery Mechanism] # function Get-TargetInfo { <# This command will return information about the target specified by TargetName. The iSCSI initiator service maintains a separate set of information about every target organized by each mechanism by which it was discovered. This means that each instance of a target can have different information such as target portal groups. Discovery Mechanism is an optional parameter and if not specified then only the list of discovery mechanisms for the target are displayed. If Discovery Mechanism is specified then information about the target instance discovered by that mechanism is displayed. #> [CmdletBinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string] $targetName, [Parameter()] [string] $discoveryMechanism ) process { } } #iscsicli LoginTarget # # #
# # # # ... # #iscsicli PersistentLoginTarget # # #
# # # # ... # #iscsicli QLoginTarget [CHAP Username] [CHAP Password] # function Connect-Target { <# This command will login to a target #> #iscsicli persistentlogintarget $t T * * * * * * * * * * * [* * *] * 0 [CmdletBinding( SupportsShouldProcess=$true )] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [Alias("Name")] [string] # the name of the target $targetName, [Parameter()] [string] # Username is the string that should be used as the CHAP username when logging into the target. By specifying * for this parameter, the iSCSI initiator service will use the initiator node name as the CHAP username. $username, [Parameter()] [string] # Password is the string that should be used as the target�s CHAP secret when logging into the target. The initiator will use this secret to compute a hash value based on the challenge sent by the target. $password, [Parameter()] [switch] # specify to persist the login information upon reboot $persist ) process { if( $username ) { $data = & iscsicli qlogintarget $targetName $username $password Write-Verbose "Raw iSCSIcli output: $data"; } else { $data = & iscsicli qlogintarget $targetName Write-Verbose "Raw iSCSIcli output: $data"; $username = '*'; $password = '*'; } Write-Verbose "Raw iSCSIcli output: $data"; if( $data -match 'already.+logged' ) { $s = get-session | where { $_.targetname -eq $targetName }; New-Object psobject -Property @{ SessionId=$s.SessionId; ConnectionId=$s.Connection.ConnectionID }; } else { # Session Id is 0xfffffa800f7900a8-0x4000013700000015 # Connection Id is 0xfffffa800f7900a8-0x23 ( $data | Out-String ) -replace '0x','' -replace "[`r`n]+",'=' | convertFrom-iSCSIOutput -field ' is '; } if( $persist ) { & iscsicli persistentlogintarget $targetName T * * * * * * * * * * * $username $password * * 0 | Out-Null } } } #iscsicli LogoutTarget # function Disconnect-Session { <# This command will attempt to logout of a target which was logged in via the session specified by SessionId. The iSCSI initiator service will not logout of a session if any devices exposed by it are currently in use. If the command fails then consult the system eventlog for additional information about the component that is using the device. #> [CmdletBinding( SupportsShouldProcess=$true, ConfirmImpact='high' )] param( [Parameter( Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] [string] $sessionId, [Parameter()] [switch] # specify to bypass standard PowerShell confirmation processes $force ) process { if( -not( $force -or $pscmdlet.shouldProcess( $sessionId, "Disconnect Session" ) ) ) { return; } $data = & iscsicli logouttarget $sessionId | Out-String; if( $data -notmatch 'success' ) { throw $data; } } } #iscsicli ListInitiators # function Get-Initiators { <# This command will display the list of initiator instance names that are running and operating with the iSCSI initiator service. #> [CmdletBinding()] param() process { & iscsicli listinitiators } } #iscsicli SessionList # function Get-Session { <# This command displays the list of active sessions for all initiators. Note that a session that has no connections is not connected to the target and is in a retry state. Microsoft iSCSI Initiator Version 6.1 Build 7601 Total of 2 sessions Session Id : fffffa800f7900a8-400001370000000d Initiator Node Name : iqn.1991-05.com.microsoft:archimedes Target Node Name : (null) Target Name : iqn.2008-08.com.starwindsoftware:127.0.0.1-target1 ISID : 40 00 01 37 00 00 TSID : 27 00 Number Connections : 1 Connections: Connection Id : fffffa800f7900a8-1b Initiator Portal : 0.0.0.0/58847 Target Portal : 192.168.1.108/3260 CID : 01 00 Devices: Device Type : Disk Device Number : 1 Storage Device Type : 7 Partition Number : 0 Friendly Name : ROCKET RAM DISK 1024 M SCSI Disk Device Device Description : Disk drive Reported Mappings : Port 1, Bus 0, Target Id 0, LUN 0 Location : Bus Number 0, Target Id 0, LUN 0 Initiator Name : ROOT\ISCSIPRT\0000_0 Target Name : iqn.2008-08.com.starwindsoftware:127.0.0.1-target1 Device Interface Name : \\?\scsi#disk&ven_rocket&prod_ram_disk_1024_m#1&1c121344&0&000000#{53f56307-b6bf-11d0-9 4f2-00a0c91efb8b} Legacy Device Name : \\.\PhysicalDrive1 Device Instance : 0x82c Volume Path Names : E:\ Session Id : fffffa800f7900a8-400001370000000f Initiator Node Name : iqn.1991-05.com.microsoft:archimedes Target Node Name : (null) Target Name : iqn.2008-08.com.starwindsoftware:127.0.0.1-scratch ISID : 40 00 01 37 00 00 TSID : 2b 00 Number Connections : 1 Connections: Connection Id : fffffa800f7900a8-1d Initiator Portal : 0.0.0.0/59359 Target Portal : 192.168.1.106/3260 CID : 01 00 Devices: Device Type : Disk Device Number : 2 Storage Device Type : 7 Partition Number : 0 Friendly Name : ROCKET RAM DISK 256 MB SCSI Disk Device Device Description : Disk drive Reported Mappings : Port 1, Bus 0, Target Id 1, LUN 0 Location : Bus Number 0, Target Id 1, LUN 0 Initiator Name : ROOT\ISCSIPRT\0000_0 Target Name : iqn.2008-08.com.starwindsoftware:127.0.0.1-scratch Device Interface Name : \\?\scsi#disk&ven_rocket&prod_ram_disk_256_mb#1&1c121344&0&000100#{53f56307-b6bf-11d0-9 4f2-00a0c91efb8b} Legacy Device Name : \\.\PhysicalDrive2 Device Instance : 0x8ac #> [CmdletBinding()] param( [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] [string] $sessionId = '.*' ) process { Write-Verbose "Session ID filter: $sessionId"; $data = ( & iscsicli sessionlist ) | out-string; $data = $data -replace "[`r`n]+",'=' Write-Verbose "raw sessionlist info : $data"; $sessions = $data -split "Session Id\s+:\s+"; $sessions = $sessions | where { $_ -match "Connections:" } | foreach { $session, $data = ( "Session Id : " + $_ ) -split 'Connections:', 2; $connection, $device = $data -split "Devices:", 2; Write-Verbose "session $session"; Write-Verbose "connection $connection"; Write-Verbose "device $device"; $session, $connection, $device = $session, $connection, $device | convertFrom-iSCSIOutput; $session | Add-Member -PassThru -MemberType NoteProperty -Name Connection -Value $connection | Add-Member -MemberType NoteProperty -Name Device -Value $device; $session; } if( -not $sessions ) { Write-Verbose "no sessions found" return; } $sessions | write-verbose; $sessions | where { write-verbose "filtering $($_.SessionId) by $sessionId"; $_.SessionId -match $sessionId } } } function convertFrom-iSCSIOutput { [CmdletBinding()] param( [Parameter(ValueFromPipeline=$true)] [string] $data, [Parameter()] [string] $itemDelimiter = '=', [Parameter()] [string] $fieldSeparator = ':' ) process { Write-Debug "convertFrom-iSCSIOutput ..." Write-Debug " Data: $data"; Write-Debug " Item Delimiter: $itemdelimiter"; Write-Debug " Field Separator: $fieldSeparator"; $a = @{}; $data -split $itemDelimiter | where { $_ -match "$fieldSeparator\s*" } | foreach { function add-ToA( $k, $v ) { $k = $k -replace ' ',''; Write-Debug "item key $k; value $v"; $a[$k] = $v; } Write-Debug "item entry $_"; $k,$v = $_ -split "\s*$fieldSeparator\s*",2; if( $k -match ' and ' ) { $k1, $k2 = $k -split ' and '; $v1, $v2 = $v -split '\s+',2; add-ToA $k1 $v1 add-ToA $k2 $v2 } else { add-ToA $k $v } } new-object psobject -Property $a; } } ########################################################### ## initialization Export-ModuleMember -Function '*';