FILE: C:\Windows\diagnostics\system\Networking\NetworkDiagnosticsTroubleshoot.ps1
--
# Copyright © 2008, Microsoft Corporation. All rights reserved.
#include utility functions and localization data
. .\UtilityFunctions.ps1
Import-LocalizedData -BindingVariable localizationString -FileName LocalizationData
#set the environment constants
.\UtilitySetConstants
Write-DiagProgress -activity $localizationString.progress_Diagnosing_Initializing
#reset the global NDF object
$Global:ndf = $null
$Global:previousNdf = $null
#initialize script level variables (script scope used to avoid odd powershell scope handling)
$script:ExpectingException = $false
$script:incidentID = $null
$Global:incidentData = $null #need to access this during verification as well
$script:skipRerun = $false
$script:attachTraceFile = $false
$script:isRerun = $false
#first check whether we're either elevated or a re-run scenario
&{
$prevIncidentID = 0
$prevFlags = 0
$script:ExpectingException = $true
#marked as no-ui. throws exception if not available
$SBSData = Get-DiagInput -ID "SecurityBoundarySafe"
$script:ExpectingException = $false
if($SBSData[0].Length -gt 0)
{
#Security boundary safe data is now always passed in to our script on rerun or elevation
#We use the "flags" field to determine whether it's a rerun or elevation -- if the flag doesn't match the current privilege, we elevated
"SBS Data Retrieved: " + $SBSData | convertto-xml | Update-DiagReport -id SecurityBoundarySafe -name "Security Boundary Safe" -verbosity Debug
$script:isRerun = $true
$admin = IsAdmin
SplitSBSData $SBSData[0] ([ref]$prevIncidentID) ([ref]$prevFlags)
if([System.Int32]($prevFlags) -eq [System.Int32]($admin))
{
"Previous run's privilege level flag (" + $prevFlags + ") matches our current level (IsAdmin:" + $admin +"). Determining whether it's appropriate to re-run." | convertto-xml | Update-DiagReport -id ReuseSession -name "Reusing previous session" -verbosity Debug
#same privilege level as last run, so this is a rerun rather than elevation
#should not use previous incident, but should determine whether rerun is necessary
#open the previous incident
$Global:ndf = GetExistingNDFInstance $prevIncidentID
if($Global:ndf)
{
#recover the input attributes so we don't re-prompt
$prevHelperClass = $Global:ndf.EntryPoint
$prevHelperAttributes = $Global:ndf.HelperAttributes
$Global:incidentData = @{"HelperClassName" = $prevHelperClass; "HelperAttributes" =$prevHelperAttributes}
#check whether re-diagnosis occurred during verification, if so open use the follow up session
$ndfRerun = $null
$followupIncidentID = $Global:ndf.FollowUpSession
if($followupIncidentID)
{
#if follow up incident is available, we should always reuse and return this data as the diagnostics result of this rerun
$ndfRerun = GetExistingNDFInstance $followupIncidentID
if($ndfRerun)
{
$Global:previousNdf = $Global:ndf #keep a handle to the previous NDF
$Global:ndf = $ndfRerun;
}
else
{
throw "Could not open re-run incident."
}
}
else
{
#don't have a rerun available so lets make sure it's appropriate to rerun
#Only necessary when the last action was a failed validation
$sessionStatus = $Global:ndf.SessionStatus
if(!($sessionStatus -eq $NDF_STOP_STATUS_FAILEDVALIDATE))
{
#rerun not necessary
if($sessionStatus -eq $NDF_STOP_STATUS_SUCCESSVALIDATE)
{
"Skipping rerun, previous session succeeded in resolving the problem." | convertto-xml | Update-DiagReport -id SkipRerun -name "Skip Rerun" -verbosity Debug
$script:skipRerun = $true
}
else
{
"Skipping rerun, previous session status (" + $sessionStatus + ") was not a verification failure. Will return same diagnosis as last session." | convertto-xml | Update-DiagReport -id SkipRerun -name "Skip Rerun" -verbosity Debug
$script:incidentID = $prevIncidentID
}
return;
}
else
{
"Previous session ended in a validation failure (" + $sessionStatus + ") was a verification failure, rerun will proceed." | convertto-xml | Update-DiagReport -id ValidRerun -name "Rerun is Valid" -verbosity Debug
$Global:ndf = $null
}
}
}
}
else
{
"Previous run's privilege level flag (" + $prevFlags + ") differs to our current level (IsAdmin:" + $admin +"). Reusing previous session (" + $prevIncidentID + ")" | convertto-xml | Update-DiagReport -id ReuseSession -name "Reusing previous session" -verbosity Debug
#different privilege level, reuse previous session
$script:incidentID = $prevIncidentID
#open the previous incident
$Global:ndf = GetExistingNDFInstance $prevIncidentID
#recover the input attributes so we don't re-prompt
$prevHelperClass = $Global:ndf.EntryPoint
$prevHelperAttributes = $Global:ndf.HelperAttributes
$Global:incidentData = @{"HelperClassName" = $prevHelperClass; "HelperAttributes" =$prevHelperAttributes}
}
}
}
trap [Exception]
{
if($script:ExpectingException)
{
$script:ExpectingException = $false
continue;
}
else
{
#rethrow exception
throw $_.Exception;
}
}
if($script:skipRerun)
{
return;
}
########################
$script:IT_EntryPoint = "DefaultConnectivity"
if($Global:ndf -eq $null)
{
"New Diagnostics Session." | convertto-xml | Update-DiagReport -id ReopenSessionCheck -name "Re-open Session Check" -verbosity Debug
#get entry point
&{
$script:ExpectingException = $true
$script:IT_EntryPoint = Get-DiagInput -ID "IT_EntryPoint"
$script:ExpectingException = $false
if($script:IT_EntryPoint[0].Length -eq 0)
{
"No entry point specified. Using default connectivity diagnose experience" | convertto-xml | Update-DiagReport -id EntryPoint -name "Entry Point" -description "Selected entry point." -verbosity Debug
$script:IT_EntryPoint = "DefaultConnectivity"
}
else
{
$script:IT_EntryPoint | convertto-xml | Update-DiagReport -id EntryPoint -name "Entry Point" -description "Selected entry point." -verbosity Debug
}
}
trap [Exception]
{
if($script:ExpectingException)
{
"No entry point specified. Using default connectivity diagnose experience" | convertto-xml | Update-DiagReport -id EntryPoint -name "Entry Point" -description "Selected entry point." -verbosity Debug
$script:ExpectingException = $false
continue;
}
else
{
#rethrow exception
throw $_.Exception;
}
}
}
$diagResult = $S_OK
if($Global:ndf -eq $null)
{
if($Global:incidentData -eq $null)
{
#Entry Point functions return a hash with "HelperClassName" and "HelperAttributes" values, or $null on failure
if($script:IT_EntryPoint -eq "InContext")
{
$Global:incidentData = InContextEntry
}
elseif($script:IT_EntryPoint -eq "HTTP")
{
$Global:incidentData = WebEntry
}
elseif($script:IT_EntryPoint -eq "SMB")
{
$Global:incidentData = FileSharingEntry
}
elseif($script:IT_EntryPoint -eq "NetworkAdapter")
{
$Global:incidentData = NetworkAdapterEntry
}
elseif($script:IT_EntryPoint -eq "Winsock")
{
$Global:incidentData = WinsockEntry
}
elseif($script:IT_EntryPoint -eq "Grouping")
{
$Global:incidentData = GroupingEntry
}
elseif($script:IT_EntryPoint -eq "Inbound")
{
$Global:incidentData = InboundEntry
}
elseif($script:IT_EntryPoint -eq "DirectAccess")
{
$Global:incidentData = DirectAccessEntry
if(!$Global:incidentData)
{
#not provisioned, root cause outside of NDF
Update-DiagRootCause -ID "{E42E5B5A-16E0-43f1-AB32-C94C608D269D}" -Detected $true
return;
}
}
elseif($script:IT_EntryPoint -eq "DefaultConnectivity")
{
#DefaultConnectivity experience initially diagnoses default URL
$Global:incidentData = GetWebNDFIncidentData $DefaultDiagURL $true
}
else
{
throw "Unexpected entry point specified"
}
}
if($Global:incidentData -eq $null)
{
throw "Entry Point function failure"
}
while($true)
{
#will diagnose, should attach trace file
$script:attachTraceFile = $true
$Global:incidentData["HelperClassName"] | convertto-xml | Update-DiagReport -id CreateIncidentHelperClass -name "Create Incident Helper Class" -description "CreateIncident Helper Class Value." -verbosity Debug
$Global:incidentData["HelperAttributes"] | convertto-xml | Update-DiagReport -id CreateIncidentHelperAttributes -name "Create Incident Helper Attributes" -description "CreateIncident Helper Attributes Value." -verbosity Debug
$Global:ndf = new-object -comObject ndfapi.NetworkDiagnostics.1 -strict
$Global:ndf.CreateIncident($Global:incidentData["HelperClassName"], $Global:incidentData["HelperAttributes"])
$waitHandle = $Global:ndf.Diagnose($DiagnoseWaitTime, 0)
if($waitHandle -eq $null)
{
throw "Diagnose call failed"
}
WaitWithProgress $localizationString.progress_Diagnosing_NoDetails $waitHandle $Global:ndf
#check diagnose result
$diagResult = $Global:ndf.DiagnoseResult
if(($script:IT_EntryPoint -eq "DefaultConnectivity") -and (!$script:isRerun))
{
#for the DefaultConnectivity entry point, if no issues are found then we ask a few follow up questions and potentially re-diagnose
if(($diagResult -eq $S_OK) -and ($Global:ndf.RootCauses.Count -eq 0))
{
#no issues were found, see if there's anything else to diagnose
#first add tracelog from session to report
AddTraceFileToSession $Global:ndf $localizationString.TraceFileReportName "Diagnose"
#add network config information to report
AddNetworkInfoToSession;
#do not attach file if we break out of loop rather than re-diagnose
$script:attachTraceFile = $false
#query the user for new entry point
$Global:incidentData = DefaultConnectivityFollowUpEntry
if($Global:incidentData -eq $null)
{
#nothing else to diagnose
break;
}
else
{
$script:IT_EntryPoint = "DefaultConnectivityOther"
}
}
else
{
#issues found, break out of loop
break;
}
}
else
{
#for anything other than the DefaultConnectivity entry point we only diagnose once during a run
break;
}
}
}
if($diagResult -eq $S_OK)
{
$Global:rootCauseEnum = $Global:ndf.RootCauses
if($Global:rootCauseEnum -ne $null)
{
#enumerate through root causes
$rootCauseCount = $Global:rootCauseEnum.Count
$rootCauseCount | convertto-xml | Update-DiagReport -id RootCausecount -name "Root Cause Count" -description "The following number of root causes were found." -verbosity Debug
#collect catch-all information for use later
$Global:catchAlls = @{}; #will contain the RC's with catch all repairs, indexed by HC it applies to
$catchAllsIndex = @{}; #will hold the index of the catch-all in the root cause list
$Global:catchAllsAltRC = @{}; #will contain the alternate RC ID for the catch all, if available, indexed by root cause
GetCatchAllInformation ($Global:rootCauseEnum) ([ref]$Global:catchAlls) ([ref]$catchAllsIndex) ([ref]$Global:catchAllsAltRC);
$Global:rootCauseEnum.Reset()
for($i=0; $i -lt $rootCauseCount; $i++)
{
$rootCause=$Global:rootCauseEnum.Next;
"ID:" + $rootCause.RootCauseID + " Description:" + $rootCause.Description | convertto-xml | Update-DiagReport -id RootCauseFound -name "Root Cause Found" -description "A Root Cause was found." -verbosity Debug
$rcFlags=$rootCause.Flags;
if($rcFlags -band $RCF_ISTHIRDPARTY)
{
$rootCause.RootCauseID + " is a third party root cause. Filters and catch-alls do not apply." | convertto-xml | Update-DiagReport -id ThirdPartyRootCause -name "Third Party Root Cause" -verbosity Debug
}
else
{
#check if RC is filtered
$catchAllRC = $null;
$filterMode = 0;
$filterValue = GetRootCauseFilterValue ($rootCause) ([ref]$filterMode);
if($filterValue -eq $FV_FILTERED)
{
#always drop filtered root causes
$rootCause.RootCauseID + " skipped because it is filtered." | convertto-xml | Update-DiagReport -id RootCauseSkipped -name "Root Cause Skipped" -verbosity Debug
continue;
}
elseif($filterValue -eq $FV_MISSING)
{
#if the root cause is not in the inclusion list and not filtered, get the catch all data
$className = $rootCause.ClassName;
if($className)
{
$catchAllRC = $Global:catchAlls[$className];
}
if(!$catchAllRC)
{
#no catch all found, should we ignore the RC?
if($filterMode -eq $FM_CATCHALL)
{
#we're only in catch all mode, and this RC did not have a catch all to replace it. We allow the RC through.
}
else
{
$rootCause.RootCauseID + " skipped because it is not in the inclusion list." | convertto-xml | Update-DiagReport -id RootCauseSkipped -name "Root Cause Skipped" -verbosity Debug
continue;
}
}
}
#a all catch-all RC should only be surfaced as a catch-all for another root cause
#So if this RC is not filtered, or a catch-all is in use and is the same as the current root cause,
#skip if it's all catch-alls
if(($filterValue -eq $FV_NOTFILTERED) -or ($catchAllRC -and ($catchAllRC.RootCauseID -eq $rootCause.RootCauseID)))
{
$nonCatchAllPresent = $false;
$repairEnum = $rootCause.Repairs;
$repairCount = $repairEnum.Count;
if($repairCount -gt 0)
{
$repairEnum.Reset();
for($curRep=0; $curRep -lt $repairCount; $curRep++)
{
$repair = $repairEnum.Next;
if(!($repair.Flags -band $RF_RESERVED_CA))
{
$nonCatchAllPresent = $true;
break;
}
}
if(!$nonCatchAllPresent)
{
$rootCause.RootCauseID + " skipped because it only contained catch-alls" | convertto-xml | Update-DiagReport -id RootCauseSkipped -name "Root Cause Skipped" -verbosity Debug
continue;
}
}
}
}
$rootCauseIndex = $i;
if($catchAllRC)
{
#from now on we use the catch-all RC
$rootCause = $catchAllRC;
$rootCauseIndex = $catchAllsIndex[$catchAllRC];
}
$instanceID = GetInstanceIDRC ($rootCause) ($Global:catchAlls) ($Global:catchAllsAltRC)
$repairEnum = $rootCause.Repairs
$repairCount = $repairEnum.Count
if($repairCount -eq 0)
{
$rootCause.RootCauseID | convertto-xml | Update-DiagReport -id RootCauseWithoutRepairs -name "Root Cause Without Repairs" -description "Skipped root cause without repairs." -verbosity Debug
continue;
}
&{
if($Global:previousNdf)
{
#before we add the root cause, lets make sure we shouldn't be skipping it
$skipReason = $Global:previousNdf.ShouldSkipRootCause($rootCause);
if($skipReason -ne $NDF_SKIPREASON_NONE)
{
$skipText = GetSkipReasonText($skipReason)
$rootCause.RootCauseID + " skipped due to reason: " + $skipText | convertto-xml | Update-DiagReport -id RootCauseSkipped -name "Root Cause Skipped" -verbosity Debug
continue;
}
}
#try to add root cause. an exception will be thrown
#if it is not manifested
$rcToAttach = $rootCause.RootCauseID;
if($catchAllRC)
{
#get the catch-all root cause ID if available
if($Global:catchAllsAltRC[$catchAllRC])
{
$rcToAttach = $Global:catchAllsAltRC[$catchAllRC];
"Attaching catch-all Root Cause instead. ID:" + $rcToAttach | convertto-xml | Update-DiagReport -id CatchAllRootCause -name "Catch-all Root Cause used." -verbosity Debug
}
}
#collect the replacement parameters
$params = @{"InstanceID" = $instanceID};
#Get extra replacement parameters from repair
$descEx = $rootCause.DescriptionEx
if(!($descEx -eq $null))
{
$params += GetParameters $descEx $params
}
#also need parameters for every repair
$repairEnum.Reset()
for($curRep=0; $curRep -lt $repairCount; $curRep++)
{
$repair = $repairEnum.Next
$descEx = $repair.DescriptionEx
if(!($descEx -eq $null))
{
$params += GetParameters $descEx $params
}
}
#add security boundary safe data
$data = GetSBSData $Global:ndf.IncidentID
$params.Add("SBSData",$data)
#keywords for escalation
$keywords = GetKeywords($rootCause.DescriptionEx);
if($keywords.Length -gt 0)
{
$params.Add("Keywords", $keywords);
}
$script:ExpectingException = $true;
Update-DiagRootCause -InstanceId $instanceID -ID $rcToAttach -Detected $true -Parameter $params
$script:ExpectingException = $false;
#log the parameters
$evtDesc = "Root Cause " + $rcToAttach
$params | convertto-xml | Update-DiagReport -id RootCauseParameters -name "Root Cause Parameters" -description $evtDesc -verbosity Debug
}
trap [Exception] {
if($script:ExpectingException)
{
$script:ExpectingException = $false
#add unmanifested root cause
$params = @{"InstanceID" = $instanceID};
$uRC = GetUnmanifestedRootCause $rootCause ([ref]$params) ($catchAllRC)
#pop-msg "URC " + $uRC
if(!($uRC -eq $null))
{
Update-DiagRootCause -InstanceId $instanceID -ID $uRC -Detected $true -Parameter $params
#log the parameters
$evtDesc = "Root Cause " + $rootCause.RootCauseID + " (" + $uRC + ")"
$params | convertto-xml | Update-DiagReport -id RootCauseParameters -name "Root Cause Parameters" -description $evtDesc -verbosity Debug
}
continue
}
else
{
#rethrow exception
throw $_.Exception;
}
}
}
}
}
elseif(!($diagResult -eq $S_FALSE))
{
AddTraceFileToSession $Global:ndf $localizationString.TraceFileReportName "Diagnose"
#may have failed due to service state or boot mode
#first verify that DPS is running and that we aren't in safe mode.
Write-DiagProgress -activity $localizationString.progress_Diagnosing_SafeMode
$safeMode = IsSafeMode
if($safeMode)
{
Update-DiagRootCause -ID "{6880DE42-9ED5-454a-8490-BA407BEABC22}" -Detected $true
return;
}
#check if DPS is started
Write-DiagProgress -activity $localizationString.progress_Diagnosing_DPS
$dpsStarted = IsDPSStarted
if(!$dpsStarted)
{
$dpsDisabled = IsDPSDisabled
if($dpsDisabled)
{
Update-DiagRootCause -ID "{A693C86C-3907-4467-8A92-F360C3482C01}" -Detected $true
}
else
{
Update-DiagRootCause -ID "{F4148D83-8526-48e2-B21A-01894ECCE98C}" -Detected $true
}
return;
}
throw "Diagnosis failed with HRESULT" + $diagResult
}
#finished with troubleshooting
#if we re-opened an existing incident, there's no need to retrieve the trace file
#since nothing new was done other than re-reading the diagnostics results
if($script:attachTraceFile)
{
#add tracelog to report
AddTraceFileToSession $Global:ndf $localizationString.TraceFileReportName "Diagnose"
#add network config information to report
AddNetworkInfoToSession;
}
#clear validation result in case we are re-running
$Global:ValidateResult = $null
--