Import-Module netsecurity $Strings = DATA { @{ FirewallRuleEnabled = "{0}: Firewall rule for {1} is enabled." FirewallRuleDisabled = "{0}: Firewall rule for {1} is disabled. This computer cannot participate in network transactions." CmdletFailed = "The {0} cmdlet failed. Please make sure the {1} module is installed." InvalidLocalComputer = "{0} is not a valid local computer name." RPCEndpointMapper = "RPC Endpoint Mapper" DtcIncomingConnection = "DTC incoming connections" DtcOutgoingConnection = "DTC outgoing connections" MatchingDtcNotFound = "A DTC instance with VirtualServerName {0} does not exist." InboundDisabled = "{0}: Inbound transactions are not allowed and this computer cannot participate in network transactions." OutboundDisabled = "{0}: Outbound transactions are not allowed and this computer cannot participate in network transactions." OSVersion = "{0} Operating System Version: {1}." OSQueryFailed = "Failed to query operating system of {0}." VersionNotSupported = "Testing DTC on Windows versions below {0} is not supported with this cmdlet." FailedToCreateCimSession = "Failed to create a CIM Session to {0}." NotARemoteComputer = "{0} is not a remote computer." PingingSucceeded = "Pinging computer {0} from {1} succeeded. " PingingFailed = "Pinging computer {0} from {1} failed." SameCids = "The {0} CID on {1} and {2} is the same. The CID should be unique to each computer." DiagnosticTestPrompt = "This diagnostic test will attempt to carry out a transaction propagation between {0} and {1}. It requires that a TCP port is opened on {0} so that a Test Resource Manager can participate in network transactions." DefaultPortDescription = "The default port is {0} and you can change it using the 'ResourceManagerPort' parameter and rerunning the test. " PortDescription = "You have specified {0} as the 'ResourceManagerPort'." FirewallRequest = "Please open port {0} in the firewall to proceed with the test." QueryText = "Do you want to proceed with the test?" InvalidDefaultCluster = "{0} is not the Virtual Server Name of the default DTC configured on this computer. You can use 'Set-DtcClusterDefault' cmdlet configure the default DTC on this computer." InvalidDefault = "{0} is not the Virtual Server Name of the default DTC configured on this computer. You can use 'Set-DtcDefault' cmdlet to configure the default DTC on this computer." NeedDtcSecurityFix = "DTC security settings and firewall settings should be fixed in order to complete the transactions propagation test." StartResourceManagerFailed = "Test resource manager creation failed." ResourceManagerStarted = "Test resource manager started." PSSessionCreated = "A new PSSession to {0} created." TransactionPropagated = "Transaction propagated from {0} to {1} using {2} propagation." TransactionPropagationFailed = "Transaction propagation {0} to {1} failed using {2} propagation." TestRMVerboseLog = "Test Resource Manager Verbose Log:" TestRMWarningLog = "Test Resource Manager Warning Log:" InvalidParameters = "At least one of LocalComputerName or RemoteComputerName parameters should be specified." } } function IsLocalComputer { param( $computerName = $null ) PROCESS { $found = $false $dtcs = Get-Dtc if ($dtcs -eq $null) { throw } else { if ($dtcs.GetType().IsArray) { foreach ($dtc in $dtcs) { if ([string]::Compare($dtc.VirtualServerName, $computerName, $true) -eq 0) { $found = $true break } } } else { if ([string]::Compare($dtcs.VirtualServerName, $computerName, $true) -eq 0) { $found = $true } } } return $found } } function CheckFirewallRule { param( $computerName = $null, $ruleId = $null, $ruleDesription = $null ) PROCESS { $success = $false $errorVariable = $null if ($computerName -eq $null) { $rule = Get-NetFirewallRule -ID $ruleId -ErrorVariable errorVariable } else { $rule = Get-NetFirewallRule -ID $ruleId -CimSession $computerName -ErrorVariable errorVariable } if ($rule -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.CmdletFailed, "Get-NetFirewallRule", "networksecurity")) } } else { if ($rule.Enabled -eq 1) { Write-Verbose ([string]::Format($Strings.FirewallRuleEnabled, $computerName, $ruleDesription)) $success = $true } else { Write-Warning ([string]::Format($Strings.FirewallRuleDisabled, $computerName, $ruleDesription)) } } return $success } } function CheckFirewallRules { param( $ComputerName = $null ) $rpcEPRuleId = "MSDTC-RPCSS-In-TCP" $dtcOutRule = "MSDTC-Out-TCP" $dtcInRule = "MSDTC-In-TCP" $rpcOutcss = CheckFirewallRule $ComputerName $rpcEPRuleId $Strings.RPCEndpointMapper $dtcIn = CheckFirewallRule $ComputerName $dtcInRule $Strings.DtcIncomingConnection $dtcOut = CheckFirewallRule $ComputerName $dtcOutRule $Strings.DtcOutgoingConnection return ($rpcOutcss -and $dtcOutRule -and $dtcInRule) } function FindDtc { param( $ComputerName = $null, $CimSession = $null ) $matchedDtc = $null $errorVariable = $null if ($cimsession -eq $null) { $dtcs = Get-Dtc -ErrorVariable $errorVariable } else { $dtcs = Get-Dtc -CimSession $CimSession -ErrorVariable errorVariable } if ($dtcs -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.CmdletFailed, "Get-Dtc", "MSDTC")) } } else { if ($dtcs.GetType().IsArray -eq $true) { foreach ($dtc in $dtcs) { if ($dtc.VirtualServerName -eq $computerName) { $matchedDtc = $dtc break } } } else { $matchedDtc = $dtcs } } if ($matchedDtc -eq $null) { throw ([string]::Format($Strings.MatchingDtcNotFound, $computerName)) } return $matchedDtc } function CheckDtcSecurity { param( $DtcNetworkSetting = $null, $ComputerName = $nul ) PROCESS { Write-Verbose ([string]::Format("{0}: AuthenticationLevel: {1}", $ComputerName, $DtcNetworkSetting.AuthenticationLevel)) Write-Verbose ([string]::Format("{0}: InboundTransactionsEnabled: {1}", $ComputerName, $DtcNetworkSetting.InboundTransactionsEnabled)) if ($DtcNetworkSetting.InboundTransactionsEnabled -eq $false) { Write-Warning ([string]::Format($Strings.InboundDisabled, $ComputerName)) } Write-Verbose ([string]::Format("{0}: OutboundTransactionsEnabled: {1}", $ComputerName, $DtcNetworkSetting.OutboundTransactionsEnabled)) if ($DtcNetworkSetting.OutboundTransactionsEnabled -eq $false) { Write-Warning ([string]::Format($Strings.OutboundDisabled, $ComputerName)) } Write-Verbose ([string]::Format("{0}: RemoteClientAccessEnabled: {1}", $ComputerName, $DtcNetworkSetting.RemoteClientAccessEnabled)) Write-Verbose ([string]::Format("{0}: RemoteAdministrationAccessEnabled: {1}", $ComputerName, $DtcNetworkSetting.RemoteAdministrationAccessEnabled)) Write-Verbose ([string]::Format("{0}: XATransactionsEnabled: {1}", $ComputerName, $DtcNetworkSetting.XATransactionsEnabled)) Write-Verbose ([string]::Format("{0}: LUTransactionsEnabled: {1}", $ComputerName, $DtcNetworkSetting.LUTransactionsEnabled)) return $DtcNetworkSetting.InboundTransactionsEnabled -and $DtcNetworkSetting.OutboundTransactionsEnabled } } function DoPingTest { param( $ToComputerName = $null, $FromComputerName = $null ) $filter = "Address='$ToComputerName'" $pingingResult = Get-WmiObject -Class Win32_PingStatus -Filter $filter -ComputerName $FromComputerName if ($pingingResult.StatusCode -eq 0) { Write-Verbose ([string]::Format($Strings.PingingSucceeded, $ToComputerName, $FromComputerName)) return $true; } else { Write-Warning ([string]::Format($Strings.PingingFailed, $ToComputerName, $FromComputerName)) return $false; } } function CompareCids { param( $LocalDtc = $null, $RemoteDtc = $null ) Write-Verbose ([string]::Format("{0}: OleTx: {1}", $LocalDtc.VirtualServerName, $LocalDtc.OleTxEndpointCid)) Write-Verbose ([string]::Format("{0}: OleTx: {1}", $RemoteDtc.VirtualServerName, $RemoteDtc.OleTxEndpointCid)) if ([string]::Compare($LocalDtc.OleTxEndpointCid, $RemoteDtc.OleTxEndpointCid, $true) -eq 0) { throw ([string]::Format($Strings.SameCids, "OleTx", $LocalDtc.VirtualServerName, $RemoteDtc.VirtualServerName)) } Write-Verbose ([string]::Format("{0}: XA: {1}", $LocalDtc.VirtualServerName, $LocalDtc.XAEndpointCid)) Write-Verbose ([string]::Format("{0}: XA: {1}", $RemoteDtc.VirtualServerName, $RemoteDtc.XAEndpointCid)) if ([string]::Compare($LocalDtc.XAEndpointCid, $RemoteDtc.XAEndpointCid, $true) -eq 0) { throw ([string]::Format($Strings.SameCids, "XA", $LocalDtc.VirtualServerName, $RemoteDtc.VirtualServerName)) } Write-Verbose ([string]::Format("{0}: UIS: {1}", $LocalDtc.VirtualServerName, $LocalDtc.UisEndpointCid)) Write-Verbose ([string]::Format("{0}: UIS: {1}", $RemoteDtc.VirtualServerName, $RemoteDtc.UisEndpointCid)) if ([string]::Compare($LocalDtc.UisEndpointCid, $RemoteDtc.UisEndpointCid, $true) -eq 0) { throw ([string]::Format($Strings.SameCids, "UIS", $LocalDtc.VirtualServerName, $RemoteDtc.VirtualServerName)) } } function CheckIfDefaultDtcConfigured { param( $ComputerName = $null, $Cimsession = $null ) $errorVariable = $null $cluster = $false if ($Cimsession -eq $null) { $dtcs = Get-Dtc -ErrorVariable errorVariable } else { $dtcs = Get-Dtc -ErrorVariable errorVariable -CimSession $Cimsession } if ($dtcs -eq $null) { if ($errorvariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.CmdletFailed, "Get-Dtc","MSDTC")) } } $errorVariable = $null if ($dtcs.GetType().IsArray) { $cluster = $true if ($Cimsession -eq $null) { $defaultDtc = Get-DtcClusterDefault -ErrorVariable errorVariable } else { $defaultDtcResource = Get-DtcClusterDefault -ErrorVariable errorVariable -CimSession $Cimsession } foreach ($dtc in $dtcs) { if ([string]::Compare($dtc.DtcName, $defaultDtcResource, $true) -eq 0) { $defaultDtc = $dtc.VirtualServerName } } } elseif ([string]::Compare($dtcs.DtcName, "Local", $true) -ne 0) { $cluster = $true #there is only one dtc instance and it is clustered $defaultDtc = $dtc.VirtualServerName } else { if ($Cimsession -eq $null) { $defaultDtc = Get-DtcDefault -ErrorVariable errorVariable } else { $defaultDtc = Get-DtcDefault -ErrorVariable errorVariable -CimSession $Cimsession } } if ($defaultDtc -eq $null) { if ($errorvariable -ne $null) { throw $errorVariable } else { if ($cluster -eq $true) { throw ([string]::Format($Strings.CmdletFailed, "Get-DtcClusterDefault", "MSDTC")) } else { throw ([string]::Format($Strings.CmdletFailed, "Get-DtcDefault", "MSDTC")) } } } else { if ([string]::Compare($defaultDtc, $ComputerName, $true) -ne 0) { if ($cluster -eq $true) { throw ([string]::Format($Strings.InvalidDefaultCluster, $ComputerName)) } else { throw ([string]::Format($Strings.InvalidDefault, $ComputerName)) } } } } function DoPingTest { param( $ToComputerName = $null, $FromComputerName = $null ) $filter = "Address='$ToComputerName'" $pingingResult = Get-WmiObject -Class Win32_PingStatus -Filter $filter -ComputerName $FromComputerName if ($pingingResult.StatusCode -eq 0) { Write-Verbose ([string]::Format($Strings.PingingSucceeded, $ToComputerName, $FromComputerName)) return $true; } else { Write-Warning ([string]::Format($Strings.PingingFailed, $ToComputerName, $FromComputerName)) return $false; } } function IsVersionSupported { param( $ComputerName = $null ) $minSupportedVersion = New-Object -TypeName System.Version -ArgumentList "6.2" $errorVariable = $null $operatngSystem = Get-WmiObject -ComputerName $ComputerName -Class WIn32_OperatingSystem -ErrorVariable errorVariable if ($operatngSystem -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.OSQueryFailed, $ComputerName)) } } else { $version = New-Object -TypeName System.Version -ArgumentList $operatngSystem.Version Write-Verbose ([string]::Format($Strings.OSVersion, $ComputerName, $version)) if ($version -ge $minSupportedVersion) { return $true; } else { return $false; } } } function TestLocalComputer { param( $ComputerName = $null ) PROCESS { $errorVariable = $null $firewallOK = CheckFirewallRules $null $dtc = FindDtc $ComputerName $null $networkSetting = Get-DtcNetworkSetting -DtcName $dtc.DtcName -ErrorVariable errorVariable if ($networkSetting -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.CmdletFailed, "Get-DtcNetworkSetting", "MSDTC")) } } $networkOk = CheckDtcSecurity $networkSetting $ComputerName return $networkOk -and $firewallOK } } function TestRemoteComputer { param( $ComputerName = $null, $CimSession = $null ) PROCESS { if ((IsVersionSupported $ComputerName) -eq $false) { throw ([string]::Format($Strings.VersionNotSupported, "6.2")) } $firewallOK = CheckFirewallRules $ComputerName $dtc = FindDtc $ComputerName $CimSession $networkSetting = Get-DtcNetworkSetting -DtcName $dtc.DtcName -CimSession $CimSession -ErrorVariable errorVariable if ($networkSetting -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.CmdletFailed, "Get-DtcNetworkSetting", "MSDTC")) } } $networkOk = CheckDtcSecurity $networkSetting $ComputerName return $networkOk -and $firewallOK } } function TestTransactionsPropagation { param( $LocalComputerName = $null, $RemoteComputerName = $null, $ResourceManagerPort = $null ) $rm = $null; $pssession = $null; try { $errorVariable $rm = Start-DtcDiagnosticResourceManager -Port $ResourceManagerPort -ErrorVariable errorVariable if ($rm -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.StartResourceManagerFailed)) } } Write-Verbose $Strings.ResourceManagerStarted $pssession = New-PSSession -ComputerName $RemoteComputerName Write-Verbose ([string]::Format($Strings.PSSessionCreated, $RemoteComputerName)) Invoke-Command -Session $pssession -ScriptBlock {Import-Module msdtc} -ErrorVariable errorVariable if ($errorVariable -ne $null) { throw $errorVariable } $errorVariable = $null $tx = Invoke-Command -Session $pssession -ScriptBlock {Receive-DtcDiagnosticTransaction -ComputerName $args[0] -Port $args[1] -PropagationMethod Pull} -args $LocalComputerName,$ResourceManagerPort -ErrorVariable errorVariable if ($tx -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.TransactionPropagationFailed, $LocalComputerName, $RemoteComputerName, "PULL")) } } Write-Verbose ([string]::Format($Strings.TransactionPropagated, $LocalComputerName, $RemoteComputerName, "PULL")) $tx = $null $tx=Invoke-Command -Session $pssession -ScriptBlock {Receive-DtcDiagnosticTransaction -ComputerName $args[0] -Port $args[1] -PropagationMethod Push} -args $LocalComputerName,$ResourceManagerPort -ErrorVariable errorVariable if ($tx -eq $null) { if ($errorVariable -ne $null) { throw $errorVariable } else { throw ([string]::Format($Strings.TransactionPropagationFailed, $LocalComputerName, $RemoteComputerName, "PUSH")) } } Write-Verbose ([string]::Format($Strings.TransactionPropagated, $LocalComputerName, $RemoteComputerName, "PUSH")) } finally { if ($rm -ne $null) { Write-Verbose $Strings.TestRMVerboseLog foreach ( $verbose in $rm.Verbose) { Write-Verbose $verbose } if ($rm.Warning -ne $null) { Write-Warning $Strings.TestRMWarningLog foreach ( $warning in $rm.Warning) { Write-Warning $warning } } Stop-DtcDiagnosticResourceManager -Job $rm } if ($pssession -ne $null) { Remove-PSSession $pssession } } } # .Link # http://go.microsoft.com/fwlink/?LinkID=253754 # .ExternalHelp TestDtc.psm1-help.xml function Test-Dtc { [CmdletBinding( SupportsShouldProcess=$true, ConfirmImpact="Low" )] [cmdletbinding(PositionalBinding=$false)] param( [ValidateNotNullOrEmpty()] [string] $LocalComputerName = $null, [ValidateNotNullOrEmpty()] [string] $RemoteComputerName = $null, [ValidateNotNullOrEmpty()] [int] [ValidateRange(0, 65535)] $ResourceManagerPort = $null ) BEGIN { } PROCESS { Import-LocalizedData -bindingVariable Strings -ErrorAction SilentlyContinue if ([string]::IsNullOrEmpty($LocalComputerName)) { if ([string]::IsNullOrEmpty($RemoteComputerName)) { throw $Strings.InvalidParameters } else { if (IsLocalComputer $RemoteComputerName) { throw ([string]::Format($Strings.NotARemoteComputer, $RemoteComputerName)) } $errorvariabe = $null $cimSession = New-CimSession -ComputerName $RemoteComputerName if ($cimSession -eq $null) { throw ([string]::Format($Strings.FailedToCreateCimSession, $ComputerName)) } try { $remoteTest = TestRemoteComputer $RemoteComputerName $cimSession } finally { Remove-CimSession -CimSession $cimSession } } } else { if ([string]::IsNullOrEmpty($RemoteComputerName)) { if (IsLocalComputer $LocalComputerName) { $localTest = TestLocalComputer $LocalComputerName } else { throw ([string]::Format($Strings.InvalidLocalComputer, $LocalComputerName)) } } else { if ((IsLocalComputer $LocalComputerName) -eq $false) { throw ([string]::Format($Strings.InvalidLocalComputer, $LocalComputerName)) } $DefaultResourceManagerPort = 3002 $remoteComputerTestable = $false $localToRemotePinging = $false $remoteToLocalPinging = $false $localComputerTestable = $false $port = $DefaultResourceManagerPort $localComputerTestable = TestLocalComputer $LocalComputerName if (IsLocalComputer $RemoteComputerName) { throw ([string]::Format($Strings.NotARemoteComputer, $RemoteComputerName)) } $errorvariabe = $null $cimSession = New-CimSession -ComputerName $RemoteComputerName if ($cimSession -eq $null) { throw ([string]::Format($Strings.FailedToCreateCimSession, $RemoteComputerName)) } try { $remoteComputerTestable = TestRemoteComputer $RemoteComputerName $cimSession $localToRemotePinging = DoPingTest $LocalComputerName $RemoteComputerName $remoteToLocalPinging = DoPingTest $RemoteComputerName $LocalComputerName $localDtc = FindDtc $LocalComputerName $null $remoteDtc = FindDtc $RemoteComputerName $cimSession CompareCids $localDtc $remoteDtc if (($localComputerTestable -and $remoteComputerTestable -and $localToRemotePinging -and $remoteToLocalPinging) -eq $false) { throw $Strings.NeedDtcSecurityFix } $diagnosticTestPrompt = ([string]::Format($Strings.DiagnosticTestPrompt, $LocalComputerName, $RemoteComputerName)) $portDescription = $null if ($ResourceManagerPort -eq $null) { $portDescription = ([string]::Format($Strings.DefaultPortDescription, $port)) } else { $port = $ResourceManagerPort $portDescription = ([string]::Format($Strings.PortDescription, $port)) } $firewallRequest = ([string]::Format($Strings.FirewallRequest, $port)) $completePrompt = ([string]::Format("{0} {1} {2}", $diagnosticTestPrompt, $portDescription, $firewallRequest)) if ($pscmdlet.ShouldProcess($completePrompt, $Strings.QueryText, $completePrompt)) { CheckIfDefaultDtcConfigured $LocalComputerName $null CheckIfDefaultDtcConfigured $RemoteComputerName $cimSession TestTransactionsPropagation $LocalComputerName $RemoteComputerName $port } } finally { Remove-CimSession -CimSession $cimSession } } } } END { } }