#/*++
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Abstract:
#
# This PowerShell script implements a mechanism to persist
# iSCSI Target settings for Import/Export across machines.
#
#--*/
#
# Setting aliases for Resize-IscsiVirtualDisk cmdlets for backward compatibility.
#
Set-Alias -Name Expand-IscsiVirtualDisk -Value Resize-IscsiVirtualDisk
Export-ModuleMember -Alias "Expand-IscsiVirtualDisk" -Cmdlet "Resize-IscsiVirtualDisk"
#
# Localized Data table for the format strings
#
Import-LocalizedData -BindingVariable msgTable
#
# templates for property-like XML manipulation of InitiatorId
#
$InitiatorIDTemplate = @'
'@;
#
# templates for property-like XML manipulation of LunMapping
#
$LunMappingTemplate = @'
'@;
#
# templates for property-like XML manipulation of iSCSI Target
#
$TargetTemplate = @'
'@;
#
# templates for property-like XML manipulation of VirtualDisk
#
$VirtualDiskTemplate = @'
'@;
#
# Flag used to test for Exported WTD
#
[System.UInt32]$LunFlagShadowLun = 0x00000002;
#
# Cluster Resource State
#
[System.Int32]$ClusterResourceStateOnline = 2;
#
# Types of VHD
# Before WinBlue, fixed is 1, diff is 2
# In WinBlue, fixed is 2, differencing is 3, dynamic is 4
#
[System.Int32]$VhdTypeFixedVhd = 1;
[System.Int32]$VhdTypeDifferencingVhd = 2;
[System.Int32]$WinBlueVhdTypeFixedVhd = 2;
[System.Int32]$WinBlueVhdTypeDynamicVhd = 3;
[System.Int32]$WinBlueVhdTypeDifferencingVhd = 4;
#
# Hash Tables used for IDMethods
#
$IDNumberToMethod = @{
0 = "NetBiosName";
1 = "DNSName";
2 = "IPAddress";
3 = "MACAddress";
4 = "IQN";
5 = "IPV6Address";
};
$IDMethodToNumber = @{
"NetBiosName" = 0;
"DNSName" = 1;
"IPAddress" = 2;
"MACAddress" = 3;
"IQN" = 4;
"IPV6Address" = 5;
};
$iSCSITargetServerResourceTypeName = 'iSCSI Target Server'
################################################################
# Description
# Given a computer name (in the form of IP-Address, NetBIOS or DNS name)
# return if the computer is reachable by RPC/WinMgmt means
#
# Parameter Computername
# String with a computer name, in NetBIOS, DNS, Ipv4 or IPv6 format
#
# Parameter isReachable
# Reference to a bool variable that will be set to true if the machine
# is in a cluster
#
# Parameter PSCredential
# Optional credentials to be used in the connection to the
# WinMmgt service of the specified machine. If null,
# the current user credentials will be implicitly used
#
################################################################
function IsReachable
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[string]
$ComputerName,
[Parameter(Position=1,Mandatory=$true)]
[ref]
$isReachable,
[Parameter(Position=2)]
[System.Management.Automation.PSCredential]
$PSCredential = $null
)
$RootWmiNSObj = $null;
if ($null -ne $PSCredential)
{
$RootWmiNSObj = Get-WmiObject -namespace root -class __NAMESPACE -Filter "Name = 'wmi'" -ComputerName $ComputerName -Authentication PacketPrivacy -Credential $PSCredential -ErrorAction SilentlyContinue -ErrorVariable ResolveComputerError;
}
else
{
$RootWmiNSObj = Get-WmiObject -namespace root -class __NAMESPACE -Filter "Name = 'wmi'" -ComputerName $ComputerName -Authentication PacketPrivacy -ErrorAction SilentlyContinue -ErrorVariable ResolveComputerError;
}
if ( $null -eq $RootWmiNSObj )
{
$isReachable.Value = $false;
}
else
{
$isReachable.Value = $true;
}
return;
}
################################################################
# Description
# Given a computer name (in the form of IP-Address, NetBIOS or DNS name)
# return if the computer is member of a cluster, and return the cluster name
#
# Parameter Computername
# String with a computer name, in NetBIOS, DNS, Ipv4 or IUPv6 format
#
# Parameter ClusterName
# Reference to a variable that will be set to the cluster name
# if the machine is in a cluster
#
# Parameter isCluster
# Reference to a bool variable that will be set to true if the machine
# is in a cluster
#
# Parameter PSCredential
# Optional credentials to be used in the connection to the
# WinMmgt service of the specified machine. If null,
# the current user credentials will be implicitly used
#
################################################################
function IsCluster
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[string]
$ComputerName,
[Parameter(Position=1,Mandatory=$true)]
[ref]
$ClusterName,
[Parameter(Position=2,Mandatory=$true)]
[ref]
$isCluster,
[Parameter(Position=3)]
[System.Management.Automation.PSCredential]
$PSCredential = $null
)
$MsClusterNSObj;
if ($null -ne $PSCredential)
{
$MsClusterNSObj = Get-WmiObject -namespace root -class __NAMESPACE -Filter "Name = 'MsCluster'" -ComputerName $ComputerName -Authentication PacketPrivacy -Credential $PSCredential;
}
else
{
$MsClusterNSObj = Get-WmiObject -namespace root -class __NAMESPACE -Filter "Name = 'MsCluster'" -ComputerName $ComputerName -Authentication PacketPrivacy;
}
if ( $null -eq $MsClusterNSObj )
{
$isCluster.Value = $false;
return;
}
$MsClusterNSObj = $null;
$MsClusterObj;
if ($null -ne $PSCredential)
{
$MsClusterObj = Get-WmiObject -Namespace root\MSCluster -Authentication PacketPrivacy -class MSCluster_Cluster -ComputerName $ComputerName -Credential $PSCredential -ErrorAction SilentlyContinue -ErrorVariable FetchMsClusterError;
}
else
{
$MsClusterObj = Get-WmiObject -Namespace root\MSCluster -Authentication PacketPrivacy -class MSCluster_Cluster -ComputerName $ComputerName -ErrorAction SilentlyContinue -ErrorVariable FetchMsClusterError;
}
if ($null -eq $MsClusterObj)
{
$isCluster.Value = $false;
return;
}
# return the cluster name to the by-ref parameter
$ClusterName.Value = $MsClusterObj.Name;
# return the cluster state to the by-ref parameter
$isCluster.Value = $true;
$MsClusterObj = $null;
return;
}
################################################################
# Description
# This function simplifies Get-WmiObject for the root\MsCluster namespace
# by optionally taking the PSCredential parameter.
#
# Parameter ClassName
# The name of the WinMgmt class in the CIM repository to be used
# for the enumeration
#
# Parameter ComputerName
# String with a computer name, in NetBIOS, DNS, Ipv4 or IPv6 format
#
# Parameter PSCredential
# Optional credentials to be used in the connection to the
# WinMmgt service of the specified machine. If null,
# the current user credentials will be implicitly used
#
# Parameters FilterString
# Optional fiter for the WinMgmt enumeration in WQL-syntax
#
################################################################
function
Get-RootMsCluster-WmiObject
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[string]
$ClassName,
[Parameter(Position=1,Mandatory=$true)]
[string]
$ComputerName,
[Parameter(Position=2)]
[System.Management.Automation.PSCredential]
$PSCredential = $null,
[Parameter(Position=3)]
[string]
$FilterString = $null
)
if ( $null -ne $PSCredential )
{
if ( $null -ne $FilterString )
{
return Get-WmiObject -Class $ClassName -Namespace root\MsCluster -Filter $FilterString -Authentication PacketPrivacy -Computername $ComputerName -Credential $PSCredential
}
else
{
return Get-WmiObject -Class $ClassName -Namespace root\MsCluster -Authentication PacketPrivacy -Computername $ComputerName -Credential $PSCredential
}
}
else
{
if ( $null -ne $FilterString )
{
return Get-WmiObject -Class $ClassName -Namespace root\MsCluster -Filter $FilterString -Authentication PacketPrivacy -Computername $ComputerName
}
else
{
return Get-WmiObject -Class $ClassName -Namespace root\MsCluster -Authentication PacketPrivacy -Computername $ComputerName
}
}
}
################################################################
# Description
# This function simplifies Get-WmiObject for the root\wmi namespace
# by optionally taking the PSCredential parameter.
#
# Parameter ClassName
# The name of the WinMgmt class in the CIM repository to be used
# for the enumeration
#
# Parameter ComputerName
# String with a computer name, in NetBIOS, DNS, Ipv4 or IPv6 format
#
# Parameter PSCredential
# Optional credentials to be used in the connection to the
# WinMmgt service of the specified machine. If null,
# the current user credentials will be implicitly used
#
# Parameters FilterString
# Optional fiter for the WinMgmt enumeration in WQL-syntax
#
################################################################
function
Get-RootWmi-WmiObject
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[string]
$ClassName,
[Parameter(Position=1,Mandatory=$true)]
[string]
$ComputerName,
[Parameter(Position=2)]
[System.Management.Automation.PSCredential]
$PSCredential = $null,
[Parameter(Position=3)]
[string]
$FilterString = $null
)
if ( $null -ne $PSCredential )
{
if ( $null -ne $FilterString )
{
return Get-WmiObject -Class $ClassName -Namespace root\wmi -Filter $FilterString -Computername $ComputerName -Credential $PSCredential
}
else
{
return Get-WmiObject -Class $ClassName -Namespace root\wmi -Computername $ComputerName -Credential $PSCredential
}
}
else
{
if ( $null -ne $FilterString )
{
return Get-WmiObject -Class $ClassName -Namespace root\wmi -Filter $FilterString -Computername $ComputerName
}
else
{
return Get-WmiObject -Class $ClassName -Namespace root\wmi -Computername $ComputerName
}
}
}
################################################################
# Description
# This function is executed once per script execution in a clustered
# environment. It returns 3 Hashable (that are passed-in by reference).
#
# Parameter Computername
# String with a computer name, in NetBIOS, DNS, Ipv4 or IPv6 format.
#
# Parameter ClusterNodesRef
# Reference to hash table that will be filled with the cluster nodes
# indexed by node-name.
#
# Parameter ClientAccessPointsRef
# Reference to a hash table variable that will be filled with the
# client access point, indexed by the Cluster-Resource name of
# the 'Network Name'.
#
# Parameter GroupsOfNetworkNamesRef
# Reference to a hash table variable that will be filled with the
# cluster resource groups associated with Network-Names, indexed
# by the Cluster-Resource name of the 'Network Name'.
#
# Parameter PSCredential
# Optional credentials to be used in the connection to the
# WinMmgt service of the specified machine. If null,
# the current user credentials will be implicitly used.
################################################################
function GetNodesClientAccessPoints
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[string]
$ComputerName,
[Parameter(Position=1,Mandatory=$true)]
[ref]
$ClusterNodesRef,
[Parameter(Position=2,Mandatory=$true)]
[ref]
$ClientAccessPointsRef,
[Parameter(Position=3,Mandatory=$true)]
[ref]
$GroupsOfNetworkNamesRef,
[Parameter(Position=4)]
[System.Management.Automation.PSCredential]
$PSCredential = $null
)
#
# get the referenced Hash passed as a parameter
#
$ClientAccessPoints = $ClientAccessPointsRef.Value;
$ClusterNodes = $ClusterNodesRef.Value;
$GroupsOfNetworkNames = $GroupsOfNetworkNamesRef.Value;
#
# select * from MSCluster_Node
#
$ClusterNodeObjs = Get-RootMsCluster-WmiObject "MSCluster_Node" $ComputerName $PSCredential;
foreach($ClusterNodeObj in $ClusterNodeObjs)
{
$ClusterNodes[$ClusterNodeObj.Name] = @();
$ClusterNodes[$ClusterNodeObj.Name] += $ClusterNodeObj.Name;
#
# gather DNS information for a node
#
try
{
$IPHostEntry = [System.Net.Dns]::GetHostEntry($ClusterNodeObj.Name);
$ClusterNodes[$ClusterNodeObj.Name] += $IPHostEntry.HostName;
$ClusterNodes[$ClusterNodeObj.Name] += $IPHostEntry.Aliases;
foreach($IPAddress in $IPHostEntry.AddressList)
{
if ( 0 -eq [System.Int64]$IPAddress.ScopeId)
{
$ClusterNodes[$ClusterNodeObj.Name] += $IPAddress.IPAddressToString;
}
}
}
catch
{
#
# GetHostEntry could fail, we spew an warning message and this entry is skipped
#
Write-Host $([String]::Format($msgTable.NameResolutionFailed, $ClusterNodeObj.Name));
}
}
#
# Hash for the __RELPATH to Name of the 'Network Name' resource
#
$NetworkNamesRelPath = @{};
#
# Hash of the ClusterResources by __RELPATH
#
$ClusterResourcesByRelPath = @{};
#
# select * from MSCluster_Resource
#
$ClusterResources = Get-RootMsCluster-WmiObject "MSCluster_Resource" $ComputerName $PSCredential;
foreach ($ClusterResource in $ClusterResources)
{
$ClusterResourcesByRelPath[$ClusterResource.__RELPATH] = $ClusterResource;
if ($ClusterResource.Type -eq "Network Name")
{
$NetworkNamesRelPath[$ClusterResource.__RELPATH] = $ClusterResource.Name;
$ClientAccessPoints[$ClusterResource.Name] = @();
$ClientAccessPoints[$ClusterResource.Name] += $ClusterResource.PrivateProperties.Name;
$ClientAccessPoints[$ClusterResource.Name] += $ClusterResource.PrivateProperties.DnsName;
#
# gather DNS information for a network name by NetBIOS Name
#
try
{
$IPHostEntry = [System.Net.Dns]::GetHostEntry($ClusterResource.PrivateProperties.Name);
$ClientAccessPoints[$ClusterResource.Name] += $IPHostEntry.HostName;
$ClientAccessPoints[$ClusterResource.Name] += $IPHostEntry.Aliases;
foreach($IPAddress in $IPHostEntry.AddressList)
{
if ( 0 -eq [System.Int64]$IPAddress.ScopeId)
{
$ClientAccessPoints[$ClusterResource.Name] += $IPAddress.IPAddressToString;
}
}
}
catch
{
#
# GetHostEntry could fail, we spew an error message and continue processing DnsName
#
Write-Host $([String]::Format($msgTable.NameResolutionFailed, $ClusterResource.PrivateProperties.Name));
}
#
# gather DNS information for a network name by DNS Name
#
try
{
$IPHostEntry = [System.Net.Dns]::GetHostEntry($ClusterResource.PrivateProperties.DnsName);
$ClientAccessPoints[$ClusterResource.Name] += $IPHostEntry.HostName;
$ClientAccessPoints[$ClusterResource.Name] += $IPHostEntry.Aliases;
foreach($IPAddress in $IPHostEntry.AddressList)
{
if ( 0 -eq [System.Int64]$IPAddress.ScopeId)
{
$ClientAccessPoints[$ClusterResource.Name] += $IPAddress.IPAddressToString;
}
}
}
catch
{
#
# GetHostEntry could fail, we spew an error message
#
Write-Host $([String]::Format($msgTable.NameResolutionFailed, $ClusterResource.PrivateProperties.DnsName));
}
}
}
#
# Groups with network names
#
$GroupRelPathNames = @{};
#
# select * from MSCluster_ResourceGroupToResource
#
$ResourceGroupToResources = Get-RootMsCluster-WmiObject "MSCluster_ResourceGroupToResource" $ComputerName $PSCredential;
foreach($ResourceGroupToResource in $ResourceGroupToResources)
{
#
# find all of the groups that have Network Names
#
if ($NetworkNamesRelPath.Keys -contains $ResourceGroupToResource.PartComponent)
{
$GroupRelPathNames[$ResourceGroupToResource.GroupComponent] = $NetworkNamesRelPath[$ResourceGroupToResource.PartComponent];
}
}
#
# find all the resources that have the same group as the NetworkNames
#
foreach($ResourceGroupToResource in $ResourceGroupToResources)
{
if ( $GroupRelPathNames.Keys -contains $ResourceGroupToResource.GroupComponent )
{
$ResourceInSameGroup = $ClusterResourcesByRelPath[$ResourceGroupToResource.PartComponent];
$ClientAccessPoint = $GroupRelPathNames[$ResourceGroupToResource.GroupComponent];
if ($ResourceInSameGroup.Type -eq 'IP Address')
{
$ClientAccessPoints[$ClientAccessPoint] += $ResourceInSameGroup.PrivateProperties.Address;
}
elseif ($ResourceInSameGroup.Type -eq 'IPv6 Address')
{
$ClientAccessPoints[$ClientAccessPoint] += $ResourceInSameGroup.PrivateProperties.Address;
}
}
}
#
# find all of the ResourceGroups that have 'Network Names'
#
$ResourceGroups = Get-RootMsCluster-WmiObject "MSCluster_ResourceGroup" $ComputerName $PSCredential;
foreach($ResourceGroup in $ResourceGroups)
{
if ( $GroupRelPathNames.Keys -contains $ResourceGroup.__RELPATH )
{
$ResourceNameNetworkName = $GroupRelPathNames[$ResourceGroup.__RELPATH];
$GroupsOfNetworkNames[$ResourceNameNetworkName] = $ResourceGroup.Name;
}
}
}
################################################################
# Description
# This function performs exception handling for
# WinMgmt method invocation (IWbemServices::ExecMethod,
# IWbemServices::PutInstance and the such) .
# It attempts to add an error-record for the 'HashTableKey' entry
# in the supplied HasTable
#
# Parameter Exception
# A generic System.Object parameter. This allows type-checking
# inside the function, since PowerShell ErrorRecords are
# not derived from System.Exception
#
# Parameter HashTableRef
# Reference to a hash table that will be updated by this funciton
#
# Parameter HashTableKey
# Indexer of the hash table, to indicate where to add
#
# Parameter FormatString
# Format string with 2 replacement location for the error message
#
# Parameter AddToArray
# Is set to true, the hash table entry is expected to contain
# an arrany (in the Powershell '@()' sense) of messages,
# instead of a plain value
#
################################################################
function HandleWinMgmtMethodException
{
PARAM(
[Parameter(Position=0,Mandatory=$true)]
[System.Object]
$Exception,
[Parameter(Position=1,Mandatory=$true)]
[ref]
$HashTableRef,
[Parameter(Position=2,Mandatory=$true)]
[string]$HashTableKey,
[Parameter(Position=3,Mandatory=$true)]
$FormatString,
[Parameter(Position=4,Mandatory=$false)]
$AddToArray = $false
)
#
# final string
#
[string]$ComposedString;
#
# fetch the HashTable passed by reference
#
[System.Collections.Hashtable]$HashTable = $HashTableRef.Value;
#
# account for the case where the ErrorRecord has the exception
# chain is: ErrorRecor -> Exception -> InnerException
#
if($Exception -is [System.Management.Automation.ErrorRecord])
{
$Exception = $Exception.Exception;
}
if ($Exception -is [System.Management.Automation.MethodInvocationException])
{
#
# chain is: Exception -> InnerException
#
$InnerException = $Exception.InnerException;
if ( $InnerException -ne $null )
{
if ($InnerException -is [System.Runtime.InteropServices.COMException])
{
$ErrMsg = [System.Runtime.InteropServices.Marshal]::GetExceptionForHR($InnerException.ErrorCode).Message;
$ComposedString = [String]::Format($FormatString,$HashTableKey,$ErrMsg);
}
else
{
$ComposedString = [String]::Format($FormatString,$HashTableKey,$InnerException.ToString());
}
}
else
{
$ComposedString = [String]::Format($FormatString,$HashTableKey,$Exception.ToString());
}
}
else
{
$ComposedString = [String]::Format($FormatString,$HashTableKey,$Exception.ToString());
}
if ($AddToArray)
{
$HashTable[$HashTableKey] += $ComposedString;
}
else
{
$HashTable[$HashTableKey] = $ComposedString
}
}
################################################################
#
# Description
# The main script begins here
#
################################################################
#
# Import case
#
function Import-IscsiTargetServerConfiguration
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]
$Filename,
[string]
$ComputerName = ".",
[string]
$Credential = $null,
[System.Management.Automation.SwitchParameter]
[bool]
$Force = $false
)
#
# resolve the supplied path. It is unknown if the file exists at this point
#
$ResolvedFileName = Resolve-Path $Filename -ErrorAction SilentlyContinue -ErrorVariable ResolvePathError
if (!$ResolvedFileName)
{
$Filename = $ResolvePathError[0].TargetObject;
}
else
{
$Filename = $ResolvedFileName;
}
#
# test the supplied file path
#
if (!$(Test-Path $Filename))
{
Write-Host $([String]::Format($msgTable.FileNotFound,$Filename));
return;
}
#
# create the credentials, if required
#
[System.Management.Automation.PSCredential]$PSCredential = $null;
if ( ($null -ne $Credential) -and ($Credential -ne ""))
{
$PSCredential = Get-Credential -Credential $Credential;
}
#
# convert from System.Management.Automation.SwitchParameter
#
[bool]$ForceFlag = $Force.ToBool();
#
# check for reachability
#
[bool]$isReachable = $false;
IsReachable $ComputerName ([ref]$isReachable) $PSCredential > $null;
if ( !$isReachable )
{
Write-Host $([String]::Format($msgTable.ComputerNotReachable,$ComputerName));
return
}
#
# check for cluster
#
[string]$ClusterName = ".";
[bool]$isCluster = $false;
IsCluster $ComputerName ([ref]$ClusterName) ([ref]$isCluster) $PSCredential > $null;
#
# get the WT_General object as a test for iSCSI Target
#
$wtGeneral = Get-RootWmi-WmiObject "WT_General" $ComputerName $PSCredential;
if ($null -eq $wtGeneral)
{
Write-Host $msgTable.TargetNotInstalled;
return
}
#
# check the version of the destination server
#
if ( !($wtGeneral.Version -match '^v6.3') )
{
#
# disallow importing on anything that is not iSCSI Target 6.3
#
Write-Host $([String]::Format($msgTable.VersionUnsupported,$wtGeneral.Version));
return
}
#
# create the DOM and load the settings file
#
[xml]$xml = New-Object XML;
$xml.Load($Filename);
#
# figure-out the source version
#
[bool]$isZamfir = $false;
[bool]$isWin8 = $false;
[bool]$isWinBlue = $false;
[string]$SettingsVersion = $xml.iSCSITargetServiceSetting.Version;
if ( $SettingsVersion -match '^v3.3' )
{
$isZamfir = $true;
}
elseif( $SettingsVersion -match '^v6.1' )
{
$isWin8 = $true;
}
elseif( $SettingsVersion -match '^v6.2' )
{
$isWin8 = $true;
}
elseif( $SettingsVersion -match '^v6.3' )
{
$isWinBlue = $true;
}
else
{
Write-Host $([String]::Format($msgTable.SettingsVersionUnsupported,$SettingsVersion));
return
}
#
# print general information
#
Write-Host $([String]::Format($msgTable.ImportHeader,$Filename));
#
# if in cluster get the resource groups for the current node and XML-set
# to detect duplication with the import set
#
$currentResourceGroup = @{};
#
# if in Cluster, gather the client access points
#
$ClientAccessPoints = @{};
$ClusterNodes = @{};
$ResourceGroupsOfNetworkNames = @{};
#
# if in cluster, maintain the not-found the resource groups
#
$NotFoundMigrationResourceGroups = @{};
if ($isCluster)
{
#
# In win8 and above, if not all the resource groups are online, we bail out
#
#
# select * from MSCluster_Resource where resource type equals to "iSCSI Target Server"
#
if (-not $isZamfir)
{
$iSCSITargetServerResources = Get-RootMsCluster-WmiObject "MSCluster_Resource" $ComputerName $PSCredential "Type='$iSCSITargetServerResourceTypeName'";
foreach ($iSCSITargetServerResource in $iSCSITargetServerResources)
{
if ($iSCSITargetServerResource.State -ne $ClusterResourceStateOnline)
{
Write-Host $([String]::Format($msgTable.AllTargetServerResourcesMustBeOnline,$iSCSITargetServerResource.Name));
return
}
}
}
#
# get all the ResourceGroups from this cluster
#
$resourceGroupObjs = Get-RootMsCluster-WmiObject "MSCluster_ResourceGroup" $ComputerName $PSCredential;
foreach($resourceGroupObj in $resourceGroupObjs)
{
[void]$currentResourceGroup.Add($resourceGroupObj.Name,$resourceGroupObj);
}
#
# enumerate the targets, and check for MigrationResourceGroup
#
if ( $xml.DocumentElement.iSCSITargetRecords.ChildNodes.Count -gt 0 )
{
foreach($Target in $xml.DocumentElement.iSCSITargetRecords.ChildNodes)
{
if (!($currentResourceGroup.Keys -contains $Target.MigrationResourceGroup))
{
if (!($NotFoundMigrationResourceGroups.Keys -contains $Target.MigrationResourceGroup))
{
$NotFoundMigrationResourceGroups[$Target.MigrationResourceGroup] = @();
}
$NotFoundMigrationResourceGroups[$Target.MigrationResourceGroup] += [String]::Format($msgTable.ResourceGroupNotFoundTarget,
$Target.MigrationResourceGroup,
$Target.HostName);
}
}
}
#
# enumerate the VirtualDisk, and check for MigrationResourceGroup
#
if ( $xml.DocumentElement.iSCSIVirtualDiskRecords.ChildNodes.Count -gt 0 )
{
foreach($VirtualDisk in $xml.DocumentElement.iSCSIVirtualDiskRecords.ChildNodes)
{
if (!($currentResourceGroup.Keys -contains $VirtualDisk.MigrationResourceGroup))
{
if (!($NotFoundMigrationResourceGroups.Keys -contains $VirtualDisk.MigrationResourceGroup))
{
$NotFoundMigrationResourceGroups[$VirtualDisk.MigrationResourceGroup] = @();
}
$NotFoundMigrationResourceGroups[$VirtualDisk.MigrationResourceGroup] += [String]::Format($msgTable.ResourceGroupNotFoundVirtualDisk,
$VirtualDisk.MigrationResourceGroup,
$VirtualDisk.DevicePath);
}
}
}
#
# find the client access points and nodes
#
GetNodesClientAccessPoints $ComputerName ([ref]$ClusterNodes) ([ref]$ClientAccessPoints) ([ref]$ResourceGroupsOfNetworkNames) $PSCredential;
}
#
# prepare the HashSet of the current resources - Targets
#
$currentTargetNames = @{};
$currentTargetIQNs = @{};
$TargetObjects = Get-RootWmi-WmiObject "WT_Host" $ComputerName $PSCredential;
foreach ($TargetObj in $TargetObjects)
{
if ($TargetObj -ne $null)
{
$currentTargetNames[$TargetObj.HostName] = $TargetObj;
$currentTargetIQNs[$TargetObj.TargetIQN] = $TargetObj;
}
}
#
# prepare the HashSet of the current resources - VirtualDisks
#
$currentDevicePaths = @{};
$VirtualDiskObjects = Get-RootWmi-WmiObject "WT_Disk" $ComputerName $PSCredential;
foreach($VirtualDiskObj in $VirtualDiskObjects)
{
if ($VirtualDiskObj -ne $null)
{
$currentDevicePaths[$VirtualDiskObj.DevicePath] = $VirtualDiskObj;
}
}
#
# prepare the System.Management.ManagementObject for the static method invocation
#
$Wt_DiskClass = Get-RootWmi-WmiObject "meta_class" $ComputerName $PSCredential "__class = 'WT_Disk'";
$Wt_HostClass = Get-RootWmi-WmiObject "meta_class" $ComputerName $PSCredential "__class = 'WT_Host'";
$Wt_IdMethodClass = Get-RootWmi-WmiObject "meta_class" $ComputerName $PSCredential "__class = 'WT_IdMethod'";
$Wt_LunMappingClass = Get-RootWmi-WmiObject "meta_class" $ComputerName $PSCredential "__class = 'WT_LunMapping'";
#
# HashTable mapping the old DiskId with the new DiskId
#
$oldDiskIdToNewDiskId = @{};
$oldDiskIdToDevicePath = @{};
#
# hash table used for reporting
#
$ImportedVirtualDisks = @{};
$NotImportedVirtualDisks = @{};
#
# These are the arrays for to store different types of VHD
#
$ArrayDifferencingVHDs = @();
$ArrayNonDifferencingVHDs = @();
$ArrayAllVHDs = @();
if ( $xml.DocumentElement.iSCSIVirtualDiskRecords.ChildNodes.Count -gt 0 )
{
#
# enumerate the VirtualDisk from the XML set, checking VHD types
#
foreach($VirtualDisk in $xml.DocumentElement.iSCSIVirtualDiskRecords.ChildNodes)
{
$oldDiskIdToDevicePath[$VirtualDisk.DiskId] = $VirtualDisk.DevicePath;
if ([string]::IsNullOrEmpty([string]$VirtualDisk.Type))
{
#
# unspecified VHD type can happen for cluster - offline.
# because ParentPath is not available for offline resoruces,
# assume fixed. If the file exists, then the code in
# Wt_DiskClass.NewWTDisk will import both types
#
$ArrayNonDifferencingVHDs += $VirtualDisk;
}
elseif ($isWinBlue -and ([System.Int32]$VirtualDisk.Type -eq $WinBlueVhdTypeFixedVhd))
{
$ArrayNonDifferencingVHDs += $VirtualDisk;
}
elseif ($isWinBlue -and ([System.Int32]$VirtualDisk.Type -eq $WinBlueVhdTypeDynamicVhd))
{
$ArrayNonDifferencingVHDs += $VirtualDisk;
}
elseif ($isWinBlue -and ([System.Int32]$VirtualDisk.Type -eq $WinBlueVhdTypeDifferencingVhd))
{
$ArrayDifferencingVHDs += $VirtualDisk;
}
elseif ((-not $isWinBlue) -and ([System.Int32]$VirtualDisk.Type -eq $VhdTypeFixedVhd))
{
$ArrayNonDifferencingVHDs += $VirtualDisk;
}
elseif ((-not $isWinBlue) -and ([System.Int32]$VirtualDisk.Type -eq $VhdTypeDifferencingVhd))
{
$ArrayDifferencingVHDs += $VirtualDisk;
}
else
{
$NotImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = [String]::Format($msgTable.DevicePathInvalidType,
$VirtualDisk.MigrationDevicePath,
$VirtualDisk.Type);
}
}
}
#
# Combine the arrays, and test if there is anything to do
#
$ArrayAllVHDs = $ArrayNonDifferencingVHDs + $ArrayDifferencingVHDs;
if ($ArrayAllVHDs.Count -gt 0)
{
foreach($VirtualDisk in $ArrayAllVHDs)
{
#
# check for MigrationDevicePath
#
[bool]$RunImport = $true;
if ($currentDevicePaths.keys -contains $VirtualDisk.MigrationDevicePath)
{
if ($ForceFlag)
{
$RunImport = $false;
#
# There is already a VirtualDisk, but -Force was specified.
# delete the VirtualDisk by DevicePath, then fall-through
#
[string]$DevicePathEscaped = $VirtualDisk.MigrationDevicePath;
$DevicePathEscaped = $DevicePathEscaped.Replace('\','\\');
$Filter = "DevicePath = `'$DevicePathEscaped`'";
$VirtualDiskToDelete = Get-RootWmi-WmiObject "WT_Disk" $ComputerName $PSCredential $Filter;
if ( $VirtualDiskToDelete -ne $null )
{
try
{
$VirtualDiskToDelete.Delete();
$RunImport = $true;
}
catch
{
HandleWinMgmtMethodException $_ ([ref]$NotImportedVirtualDisks) $VirtualDisk.MigrationDevicePath $msgTable.VirtualDiskDeleteInvocationFailureMsg;
}
}
else
{
$NotImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = [String]::Format($msgTable.NotDeletedDevicePathAlreadyExists,
$VirtualDisk.MigrationDevicePath);
}
}
else
{
$NotImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = [String]::Format($msgTable.DevicePathAlreadyExists,
$VirtualDisk.MigrationDevicePath);
$RunImport = $false;
}
}
if ($RunImport)
{
$ReturnObj = $null;
try
{
if (($isWinBlue -and ([System.Int32]$VirtualDisk.Type -eq $WinBlueVhdTypeDifferencingVhd)) -or ((-not $isWinBlue) -and ([System.Int32]$VirtualDisk.Type -eq $VhdTypeDifferencingVhd)))
{
$ReturnObj = $Wt_DiskClass.NewDiffWTDisk($VirtualDisk.MigrationDevicePath,
$null,
$VirtualDisk.Description);
}
else
{
$ReturnObj = $Wt_DiskClass.NewWTDisk($VirtualDisk.MigrationDevicePath,
$VirtualDisk.Description,
0);
}
if ( $null -ne $ReturnObj )
{
$CreatedWtDisk = $ReturnObj.ReturnValue;
if ( $null -ne $CreatedWtDisk )
{
#
# Save-off the generated DiskId ~ WTD
#
$oldDiskIdToNewDiskId.Add([string]$VirtualDisk.DiskId,[System.UInt32]$CreatedWtDisk.Wtd);
#
# If the VirtualDisk was disabled, disable-it now.
# if the settings were from cluster and now the environment is standalone
# the property may be undefined. Assume enable
#
[bool]$EnableState = $true;
if (![string]::IsNullOrEmpty([string]$VirtualDisk.Enabled))
{
$EnableState = [System.Convert]::ToBoolean($VirtualDisk.Enabled);
}
if (!$EnableState)
{
$Filter = "WTD = $($CreatedWtDisk.Wtd)";
$FetchedWtDisk = Get-RootWmi-WmiObject "WT_Disk" $ComputerName $PSCredential $Filter;
$FetchedWtDisk.Enabled = $EnableState;
$null = $FetchedWtDisk.Put();
}
#
# add this Devicepath to our hashtable of success case
#
$ImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = $true;
$CreatedWtDisk = $null;
}
else
{
$NotImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = [String]::Format($msgTable.NewWTDiskMethodInvocationFailure,
$VirtualDisk.MigrationDevicePath);
}
$ReturnObj = $null;
}
else
{
$NotImportedVirtualDisks[$VirtualDisk.MigrationDevicePath] = [String]::Format($msgTable.NewWTDiskMethodInvocationFailure,
$VirtualDisk.MigrationDevicePath);
}
}
catch
{
HandleWinMgmtMethodException $_ ([ref]$NotImportedVirtualDisks) $VirtualDisk.MigrationDevicePath $msgTable.NewWTDiskMethodInvocationFailureMsg;
}
}
}
}
#
# hash table used for reporting
#
$ImportedTargets = @{};
$NotImportedTargets = @{};
$TargetsImportErrors = @{};
#
# get the Targets from the XML-set
#
if ( $xml.DocumentElement.iSCSITargetRecords.ChildNodes.Count -gt 0 )
{
foreach($Target in $xml.DocumentElement.iSCSITargetRecords.ChildNodes)
{
[bool]$RunImport = $true;
if ($currentTargetNames.Keys -contains $Target.HostName)
{
if ($ForceFlag)
{
$RunImport = $false;
$Filter = "HostName = `'$($Target.HostName)`'";
$TargetToDelete = Get-RootWmi-WmiObject "WT_Host" $ComputerName $PSCredential $Filter;
if ( $TargetToDelete -ne $null )
{
try
{
$TargetToDelete.Delete();
$RunImport = $true;
}
catch
{
HandleWinMgmtMethodException $_ ([ref]$NotImportedTargets) $Target.HostName $msgTable.TargetDeleteInvocationFailureMsg;
}
}
else
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.NotDeletedTargetNameAlreadyExists,
$Target.HostName);
}
}
else
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.TargetNameAlreadyExists,$Target.HostName);
$RunImport = $false;
}
}
elseif ($currentTargetIQNs.keys -contains $Target.TargetIQN)
{
if ($ForceFlag)
{
$RunImport = $false;
$Filter = "TargetIQN = `'$($Target.HostName)`'";
$TargetToDelete = Get-RootWmi-WmiObject "WT_Host" $ComputerName $PSCredential $Filter;
if ( $TargetToDelete -ne $null )
{
try
{
$TargetToDelete.Delete();
$RunImport = $true;
}
catch
{
# Failure to delete in -Force mode, do nothing
}
}
if (!$RunImport)
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.NotDeletedTargetNameAlreadyExists,
$Target.HostName);
}
}
else
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.TargetIQNNameAlreadyExists,$Target.TargetIQN);
$RunImport = $false;
}
}
if ($RunImport)
{
$ReturnObj = $null;
try
{
$ReturnObj = $Wt_HostClass.NewHost($Target.HostName,
$Target.MigrationResourceGroup);
if ( $null -ne $ReturnObj )
{
$CreatedWtHost = $ReturnObj.ReturnValue;
if ( $null -ne $CreatedWtHost )
{
# add and empty array to the hashtable
$TargetsImportErrors[$Target.HostName] = @();
#
# Get back the object to modify properties
#
$Filter = "HostName = '$($Target.HostName)'";
$FetchedWtHost = Get-RootWmi-WmiObject "WT_Host" $ComputerName $PSCredential $Filter;
if (![string]::IsNullOrEmpty([string]$Target.Description))
{
$FetchedWtHost.Description = [string]$Target.Description;
}
if (![string]::IsNullOrEmpty([string]$Target.TargetIQN))
{
$FetchedWtHost.TargetIQN = [string]$Target.TargetIQN;
}
if (![string]::IsNullOrEmpty([string]$Target.NumRecvBuffers))
{
$FetchedWtHost.NumRecvBuffers = [System.UInt32]$Target.NumRecvBuffers;
}
if (![string]::IsNullOrEmpty([string]$Target.FirstBurstLength))
{
$FetchedWtHost.TargetFirstBurstLength = [System.UInt32]$Target.FirstBurstLength;
}
if (![string]::IsNullOrEmpty([string]$Target.MaxBurstLength))
{
$FetchedWtHost.TargetMaxBurstLength = [System.UInt32]$Target.MaxBurstLength;
}
if (![string]::IsNullOrEmpty([string]$Target.MaxRecvDataSegmentLength))
{
$FetchedWtHost.TargetMaxRecvDataSegmentLength = [System.UInt32]$Target.MaxRecvDataSegmentLength;
}
[bool]$EnforceIdleTimeoutDetection = $true;
if (![string]::IsNullOrEmpty([string]$Target.EnforceIdleTimeoutDetection))
{
$EnforceIdleTimeoutDetection = [System.Convert]::ToBoolean($Target.EnforceIdleTimeoutDetection);
$FetchedWtHost.EnforceIdleTimeoutDetection = $EnforceIdleTimeoutDetection;
}
try
{
$null = $FetchedWtHost.Put();
}
catch
{
HandleWinMgmtMethodException $_ ([ref]$TargetsImportErrors) $Target.HostName $msgTable.NewHostPutInvocationFailureMsg $true;
}
#
# now process the InitiatorIDs
#
foreach($InitiatorID in $Target.InitiatorIDs.ChildNodes)
{
if ([string]::IsNullOrEmpty([string]$InitiatorID.Method) -or
[string]::IsNullOrEmpty([string]$InitiatorID.Value) )
{
# skip empty records
}
#
# validate the Id-Method
#
elseif ($IDNumberToMethod.Keys -contains $InitiatorID.Method)
{
$IdMethodInstance = $Wt_IdMethodClass.CreateInstance();
$IdMethodInstance.HostName = $Target.HostName;
$IdMethodInstance.Method = [System.Int32]$InitiatorID.Method;
$IdMethodInstance.Value = $InitiatorID.Value;
$null = $IdMethodInstance.Put();
}
else
{
# unsupported Identification method
$TargetsImportErrors[$Target.HostName] += [String]::Format($msgTable.TargetIdMethodError,
$InitiatorID.Method);
}
}
#
# now process the LunMappings
#
foreach($LunMapping in $Target.LunMappings.ChildNodes)
{
if ([string]::IsNullOrEmpty([string]$LunMapping.DiskId) -or
[string]::IsNullOrEmpty([string]$LunMapping.LUN) )
{
# skip empty records
}
elseif ($oldDiskIdToNewDiskId.ContainsKey([string]$LunMapping.DiskId))
{
$LunMappingInstance = $Wt_LunMappingClass.CreateInstance();
$LunMappingInstance.HostName = $Target.HostName;
$LunMappingInstance.LUN = [System.UInt32]$LunMapping.LUN;
#
# derive the old to new mapping from the HashTable
#
$LunMappingInstance.WTD = [System.UInt32]$oldDiskIdToNewDiskId.get_Item([string]$LunMapping.DiskId);
$null = $LunMappingInstance.Put();
}
else
{
$TargetsImportErrors[$Target.HostName] += [String]::Format($msgTable.TargetLunMappingError,
$LunMapping.DiskId);
}
}
#
# If the Target was disabled, disable-it now.
# if the settings were from cluster and now the environment is standalone
# the property may be undefined. Assume enable
#
[bool]$EnableState = $true;
if (![string]::IsNullOrEmpty([string]$Target.Enabled))
{
$EnableState = [System.Convert]::ToBoolean($Target.Enabled);
}
if (!$EnableState)
{
$FetchedWtHost = Get-RootWmi-WmiObject "WT_Host" $ComputerName $PSCredential $Filter;
$FetchedWtHost.Enabled = $EnableState;
$null = $FetchedWtHost.Put();
}
#
# Print a message for Chap/ReverseChap
#
if (![string]::IsNullOrEmpty($Target.EnableCHAP) -and
[System.Convert]::ToBoolean($Target.EnableCHAP))
{
$TargetsImportErrors[$Target.HostName] += [String]::Format($msgTable.TargetChapError,
$Target.CHAPUserName);
}
if (![string]::IsNullOrEmpty($Target.EnableReverseCHAP) -and
[System.Convert]::ToBoolean($Target.EnableReverseCHAP))
{
$TargetsImportErrors[$Target.HostName] += [String]::Format($msgTable.TargetReverseChapError,
$Target.ReverseCHAPUserName);
}
#
# save this target as the successfully imported ones
#
$ImportedTargets[$Target.HostName] = $true;
}
else
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.NewHostMethodInvocationFailure,
$Target.HostName);
}
$ReturnObj = $null;
}
else
{
$NotImportedTargets[$Target.HostName] = [String]::Format($msgTable.NewHostMethodInvocationFailure,
$Target.HostName);
}
}
catch
{
HandleWinMgmtMethodException $_ ([ref]$NotImportedTargets) $Target.HostName $msgTable.NewHostMethodInvocationFailureMsg;
}
}
}
}
#
# print the mismatched resource groups
#
if($isCluster -and ($NotFoundMigrationResourceGroups.Count -gt 0))
{
Write-Host $([String]::Format($msgTable.FooterNotImportedResourceGroups,$NotFoundMigrationResourceGroups.Count));
foreach ($KeyVal in $NotFoundMigrationResourceGroups.GetEnumerator())
{
Write-Host " $($KeyVal.Key) - $($KeyVal.Value)"
}
}
#
# print the Imported iSCSI Target
#
if ($ImportedTargets.Count -gt 0)
{
Write-Host $([String]::Format($msgTable.FooterImportedTargets,$ImportedTargets.Count));
foreach ($KeyVal in $ImportedTargets.GetEnumerator())
{
Write-Host " $($KeyVal.Key)";
if ($TargetsImportErrors.ContainsKey($KeyVal.Key))
{
$TargetErrorMessages = $TargetsImportErrors[$KeyVal.Key];
if ($TargetErrorMessages.Count -gt 0)
{
Write-Host $($msgTable.ErrorHeader);
foreach($TargetErrorMessage in $TargetErrorMessages)
{
Write-Host $([String]::Format($msgTable.TargetSingleError,$TargetErrorMessage));
}
}
}
}
}
if ($NotImportedTargets.Count -gt 0)
{
Write-Host $([String]::Format($msgTable.FooterNotImportedTargets,$NotImportedTargets.Count));
foreach ($KeyVal in $NotImportedTargets.GetEnumerator())
{
Write-Host $([String]::Format($msgTable.TargetNameError,$KeyVal.Key,$KeyVal.Value));
}
}
#
# print a report of the VHD actually imported from the settings
#
if ($ImportedVirtualDisks.Count -gt 0)
{
Write-Host $([String]::Format($msgTable.FooterImportedVirtualDisks,$ImportedVirtualDisks.Count));
foreach ($KeyVal in $ImportedVirtualDisks.GetEnumerator())
{
Write-Host " $($KeyVal.Key)";
}
}
if ($NotImportedVirtualDisks.Count -gt 0)
{
Write-Host $([String]::Format($msgTable.FooterNotImportedVirtualDisks,$NotImportedVirtualDisks.Count));
foreach ($KeyVal in $NotImportedVirtualDisks.GetEnumerator())
{
Write-Host $([String]::Format($msgTable.VirtualDiskNameError,$KeyVal.Key,$KeyVal.Value));
}
}
}
#
# Export case
#
function Export-IscsiTargetServerConfiguration
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]
$Filename,
[string]
$ComputerName = ".",
[string]
$Credential = $null,
[System.Management.Automation.SwitchParameter]
[bool]
$Force = $false
)
#
# resolve the supplied path. It is unknown if the file exists at this point
#
$ResolvedFileName = Resolve-Path $Filename -ErrorAction SilentlyContinue -ErrorVariable ResolvePathError
if (!$ResolvedFileName)
{
$Filename = $ResolvePathError[0].TargetObject;
}
else
{
$Filename = $ResolvedFileName;
}
#
# convert from System.Management.Automation.SwitchParameter
#
[bool]$ForceFlag = $Force.ToBool();
#
# test the supplied file path
#
[bool]$FileNameTest = $(Test-Path $Filename);
if ( $FileNameTest -and !$ForceFlag )
{
Write-Host $([String]::Format($msgTable.SettingsFileExists,$Filename));
return;
}
#
# test the folder to the supplied file path
#
$FolderPath = Split-Path $Filename;
$FileNameTest = $(Test-Path $FolderPath);
if (!$FileNameTest)
{
Write-Host $([String]::Format($msgTable.SettingsFileFolderNotFound,$FolderPath));
return;
}
#
# create the credentials, if required
#
[System.Management.Automation.PSCredential]$PSCredential = $null;
if ( ($null -ne $Credential) -and ($Credential -ne ""))
{
$PSCredential = Get-Credential -Credential $Credential;
}
#
# check for reachability
#
[bool]$isReachable = $false;
IsReachable $ComputerName ([ref]$isReachable) $PSCredential > $null;
if ( !$isReachable )
{
Write-Host $([String]::Format($msgTable.ComputerNotReachable,$ComputerName));
return
}
#
# check for cluster
#
[string]$ClusterName = ".";
[bool]$isCluster = $false;
IsCluster $ComputerName ([ref]$ClusterName) ([ref]$isCluster) $PSCredential > $null;
#
# if in Cluster, gather the client access points and the nodes
#
$ClientAccessPoints = @{};
$ClusterNodes = @{};
$ResourceGroupsOfNetworkNames = @{};
#
# matching type, if any
#
$NodeMatched = $null;
$ClientAccessPointMatched = $null;
$ClusterNameMatched = $null;
$FilterResourceGroup = $null;
if ($isCluster)
{
#
# find the client access points and node names
#
GetNodesClientAccessPoints $ComputerName ([ref]$ClusterNodes) ([ref]$ClientAccessPoints) ([ref]$ResourceGroupsOfNetworkNames) $PSCredential;
$ComputerNameStr = $ComputerName;
if ($ComputerNameStr -eq ".")
{
$ComputerNameStr = $env:COMPUTERNAME;
}
#
# find which class of cluster access point the ComputerName belongs to
#
foreach ($KeyVal in $ClusterNodes.GetEnumerator())
{
if ($KeyVal.Value -contains $ComputerNameStr)
{
$NodeMatched = $KeyVal.Key;
break;
}
};
if ( $null -eq $NodeMatched )
{
foreach ($KeyVal in $ClientAccessPoints.GetEnumerator())
{
if ($KeyVal.Value -contains $ComputerNameStr)
{
$ClientAccessPointMatched = $KeyVal.Key;
break;
}
};
}
if ($null -ne $ClientAccessPointMatched )
{
#
# for the 'Cluster' Name case, dafault to a special-case `node`
#
if ($ClientAccessPointMatched -eq 'Cluster Name')
{
$ClusterNameMatched = $ClusterName;
$ClientAccessPointMatched = $null;
}
}
if ($null -ne $ClientAccessPointMatched)
{
$FilterResourceGroup = $ResourceGroupsOfNetworkNames[$ClientAccessPointMatched];
}
}
#
# get the version
#
$wtGeneral = Get-RootWmi-WmiObject "WT_General" $ComputerName $PSCredential;
if ($null -eq $wtGeneral)
{
Write-Host $msgTable.TargetNotInstalled;
return;
}
[bool]$isZamfir = $false;
[bool]$isWin8 = $false;
[bool]$isWinBlue = $false;
if( $wtGeneral.Version -match '^v3.3' )
{
$isZamfir = $true
}
elseif( $wtGeneral.Version -match '^v6.2' )
{
$isWin8 = $true
}
elseif( $wtGeneral.Version -match '^v6.3' )
{
$isWinBlue = $true
}
else
{
Write-Host $([String]::Format($msgTable.VersionUnsupported,$wtGeneral.Version));
return
}
if ($isCluster -and (-not $isZamfir))
{
#
# In win8 and above, if not all the resource groups are online, we bail out
#
#
# select * from MSCluster_Resource where resource type equals to "iSCSI Target Server"
#
$iSCSITargetServerResources = Get-RootMsCluster-WmiObject "MSCluster_Resource" $ComputerName $PSCredential "Type='$iSCSITargetServerResourceTypeName'";
foreach ($iSCSITargetServerResource in $iSCSITargetServerResources)
{
if ($iSCSITargetServerResource.State -ne $ClusterResourceStateOnline)
{
Write-Host $([String]::Format($msgTable.AllTargetServerResourcesMustBeOnline, $iSCSITargetServerResource.Name));
return
}
}
}
#
# Print general information
#
if ($isCluster)
{
if ( $null -ne $NodeMatched )
{
Write-Host $([String]::Format($msgTable.HeaderNodeName,$Filename,$ClusterName,$NodeMatched));
}
elseif ( $null -ne $ClusterNameMatched )
{
Write-Host $([String]::Format($msgTable.HeaderClusterName,$Filename,$ClusterName,$NodeMatched));
}
elseif ( $null -ne $ClientAccessPointMatched )
{
Write-Host $([String]::Format($msgTable.HeaderClientAccessPointName,$Filename,$ClusterName,$ClientAccessPointMatched,$FilterResourceGroup));
}
}
else
{
$ComputerNameStr = $ComputerName;
if ($ComputerNameStr -eq ".")
{
$ComputerNameStr = $env:COMPUTERNAME;
}
Write-Host $([String]::Format($msgTable.HeaderStandalone,$Filename,$ComputerNameStr));
}
#
# create the DOM
#
[xml]$xml = New-Object XML;
#
# Processing instruction and root node
#
[System.Xml.XmlProcessingInstruction]$processingInstruction = $xml.CreateProcessingInstruction("xml","version='1.0'");
$null = $xml.AppendChild($processingInstruction);
[System.Xml.XmlElement]$root = $xml.CreateElement("iSCSITargetServiceSetting");
$root.SetAttribute("xmlns","http://schemas.microsoft.com/iSCSITarget/DeploymentSettings/3.3");
$root.SetAttribute("Version",[string]$wtGeneral.Version);
$root.SetAttribute("ExportTimeStamp",$([datetime]::Now.Date.ToString("o")));
$root.SetAttribute("ComputerName",[string]$ComputerName);
$root.SetAttribute("IsCluster",([string]$isCluster));
if ($isCluster)
{
$root.SetAttribute("ClusterName",$ClusterName);
if ( $null -ne $NodeMatched )
{
$root.SetAttribute("ClusterResourcesScope","NodeName");
$root.SetAttribute("ClusterNode",$NodeMatched);
}
elseif ( $null -ne $ClusterNameMatched )
{
$root.SetAttribute("ClusterResourcesScope","ClusterName");
}
elseif ( $null -ne $ClientAccessPointMatched )
{
$root.SetAttribute("ClusterResourcesScope","ClientAccessPoint");
$root.SetAttribute("ClusterClientAccessPoint",$ClientAccessPointMatched);
}
}
$null = $xml.AppendChild($root);
#
# XmlNode holder for Target
#
$rootTargets = $xml.CreateElement("iSCSITargetRecords");
$null = $root.AppendChild($rootTargets);
#
# convert the templates into System.Xml.XmlNode
#
$InitiatorIDBaseNode = $xml.ImportNode(([xml]$InitiatorIDTemplate).DocumentElement,$true);
$LunMappingBaseNode = $xml.ImportNode(([xml]$LunMappingTemplate).DocumentElement,$true);
$TargetBaseNode = $xml.ImportNode(([xml]$TargetTemplate).DocumentElement,$true);
#
# hash table used for reporting
#
$TargetsInExportSet = @{};
$TargetObjects = Get-RootWmi-WmiObject "WT_Host" -ComputerName $ComputerName $PSCredential;
if ($null -ne $TargetObjects)
{
foreach ($TargetObj in $TargetObjects)
{
if ( $null -ne $FilterResourceGroup)
{
if ( $TargetObj.ResourceGroup -ne $FilterResourceGroup )
{
continue;
}
}
$TargetNode = $TargetBaseNode.CloneNode($true);
if ($isZamfir)
{
$TargetNode.Enabled = [string]$TargetObj.Enable;
}
else
{
$TargetNode.Enabled = [string]$TargetObj.Enabled;
}
$TargetNode.HostName = [string]$TargetObj.HostName;
$TargetNode.TargetIQN = [string]$TargetObj.TargetIQN;
$TargetNode.Description = [string]$TargetObj.Description;
$TargetNode.ResourceGroup = [string]$TargetObj.ResourceGroup;
$TargetNode.MigrationResourceGroup = [string]$TargetObj.ResourceGroup;
$TargetNode.EnforceIdleTimeoutDetection = [string]$TargetObj.EnforceIdleTimeoutDetection;
$TargetNode.FirstBurstLength = [string]$TargetObj.TargetFirstBurstLength;
$TargetNode.MaxBurstLength = [string]$TargetObj.TargetMaxBurstLength;
$TargetNode.MaxRecvDataSegmentLength = [string]$TargetObj.TargetMaxRecvDataSegmentLength;
$TargetNode.NumRecvBuffers = [string]$TargetObj.NumRecvBuffers;
$TargetNode.EnableCHAP = [string]$TargetObj.EnableCHAP;
# secrets are not stored
#$TargetNode.CHAPSecret = [string]$TargetObj.CHAPSecret;
$TargetNode.CHAPUserName = [string]$TargetObj.CHAPUserName;
$TargetNode.EnableReverseCHAP = [string]$TargetObj.EnableReverseCHAP;
# secrets are not stored
#$TargetNode.ReverseCHAPSecret = [string]$TargetObj.ReverseCHAPSecret;
$TargetNode.ReverseCHAPUserName = [string]$TargetObj.ReverseCHAPUserName;
#
# Fetch the System.Xml.XmlNode used to append later
#
$InitiatorIDsNode = $TargetNode.SelectSingleNode("InitiatorIDs");
$LunMappingsNode = $TargetNode.SelectSingleNode("LunMappings");
#
# Initiator IDs
#
$FilterString = "HostName = '$($TargetObj.HostName)'";
$IdMethods = Get-RootWmi-WmiObject "WT_IDMethod" $ComputerName $PSCredential $FilterString;
if ($null -ne $IdMethods)
{
foreach($IdMethod in $IdMethods)
{
$InitiatorIDNode = $InitiatorIDBaseNode.CloneNode($true);
$InitiatorIDNode.Method = [string]$IdMethod.Method;
$InitiatorIDNode.Value = [string]$IdMethod.Value;
$null = $InitiatorIDsNode.AppendChild($InitiatorIDNode);
}
}
#
# Lun Mappings
#
$LunMappings = Get-RootWmi-WmiObject "WT_LunMapping" $ComputerName $PSCredential $FilterString
if ($null -ne $LunMappings)
{
foreach($LunMapping in $LunMappings)
{
$LunMappingNode = $LunMappingBaseNode.CloneNode($true);
$LunMappingNode.LUN = [string]$LunMapping.LUN;
$LunMappingNode.DiskId = [string]$LunMapping.WTD;
$null = $LunMappingsNode.AppendChild($LunMappingNode);
}
}
$null = $rootTargets.AppendChild($TargetNode);
$TargetsInExportSet[$TargetObj.HostName] = $true;
}
}
#
# XmlNode holder for VirtualDisks
#
$rootVirtualDisks = $xml.CreateElement("iSCSIVirtualDiskRecords");
$null = $root.AppendChild($rootVirtualDisks);
#
# hash table used for reporting
#
$FilesInExportSet = @{};
$FilesNotInExportSet = @{};
#
# convert the template into System.Xml.XmlNode
#
$VirtualDiskNode = $xml.ImportNode(([xml]$VirtualDiskTemplate).DocumentElement,$true);
$VirtualDiskObjects = Get-RootWmi-WmiObject "WT_Disk" $ComputerName $PSCredential;
if ($null -ne $VirtualDiskObjects )
{
foreach($VirtualDiskObj in $VirtualDiskObjects)
{
if ( $null -ne $FilterResourceGroup)
{
if ( $VirtualDiskObj.ResourceGroup -ne $FilterResourceGroup )
{
continue;
}
}
if ($VirtualDiskObj.Flags -and $LunFlagShadowLun)
{
#
# WT_Disk Exported from snapshots are excluded from the settings
#
$FilesNotInExportSet[$VirtualDiskObj.DevicePath] = $false;
}
else
{
$VirtualDiskNode = $VirtualDiskNode.CloneNode($true);
$VirtualDiskNode.DiskId = [string]$VirtualDiskObj.Wtd;
$VirtualDiskNode.Type = [string]$VirtualDiskObj.Type;
$VirtualDiskNode.Enabled = [string]$VirtualDiskObj.Enabled;
$VirtualDiskNode.DevicePath = [string]$VirtualDiskObj.DevicePath;
$VirtualDiskNode.ParentPath = [string]$VirtualDiskObj.ParentPath;
$VirtualDiskNode.MigrationDevicePath = [string]$VirtualDiskObj.DevicePath;
$VirtualDiskNode.MigrationParentPath = [string]$VirtualDiskObj.ParentPath;
$VirtualDiskNode.MigrationResourceGroup = [string]$VirtualDiskObj.ResourceGroup;
$VirtualDiskNode.Description = [string]$VirtualDiskObj.Description;
$VirtualDiskNode.SnapshotStorageSize = [string]$VirtualDiskObj.SnapshotStorageSizeInMB;
$null = $rootVirtualDisks.AppendChild($VirtualDiskNode);
$FilesInExportSet[$VirtualDiskObj.DevicePath] = $true;
}
}
}
$xml.Save($Filename);
#
# print a report of the Targets actually exported in the settings
#
if ( $TargetsInExportSet.Count -gt 0 )
{
Write-Host $([String]::Format($msgTable.FooterExportedTargets,$TargetsInExportSet.Count));
foreach ($KeyVal in $TargetsInExportSet.GetEnumerator())
{
Write-Host " $($KeyVal.Key)"
}
}
#
# print a report of the VHD actually exported in the settings
#
if ( $FilesInExportSet.Count -gt 0 )
{
Write-Host $([String]::Format($msgTable.FooterExportedVirtualDisks,$FilesInExportSet.Count));
foreach ($KeyVal in $FilesInExportSet.GetEnumerator())
{
Write-Host " $($KeyVal.Key)"
}
}
#
# print a report of the VHD actually NOT exported in the settings
#
if ( $FilesNotInExportSet.Count -gt 0 )
{
Write-Host $([String]::Format($msgTable.FooterNotExportedVirtualDisks,$FilesNotInExportSet.Count));
foreach ($KeyVal in $FilesNotInExportSet.GetEnumerator())
{
Write-Host " $($KeyVal.Key)"
}
}
}
#
# Export functions to be visible in the global scope.
#
Export-ModuleMember "Import-IscsiTargetServerConfiguration", "Export-IscsiTargetServerConfiguration"