{"id":5891,"date":"2015-05-28T00:01:00","date_gmt":"2015-05-28T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2015\/05\/28\/powershell-time-sync-error-handling-in-parallel-in-workflows\/"},"modified":"2019-02-18T09:47:43","modified_gmt":"2019-02-18T16:47:43","slug":"powershell-time-sync-error-handling-in-parallel-in-workflows","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-time-sync-error-handling-in-parallel-in-workflows\/","title":{"rendered":"PowerShell Time Sync: Error Handling in Parallel in Workflows"},"content":{"rendered":"<p><b style=\"font-size:12px\">Summary: <\/b><span style=\"font-size:12px\">Guest blogger, Rudolf Vesely talks about error handling in workflows and repair actions.<\/span><\/p>\n<p align=\"left\">Microsoft Scripting Guy, Ed Wilson, is here. Today is Part 2 of a 3-part series written by guest blogger Rudolf Vesely. Read yesterday&rsquo;s post to catch up and to learn more about Rudolf:<b> <\/b><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/powershell-time-sync-get-and-evaluate-synchronization-state\/\" target=\"_blank\">PowerShell Time Sync: Get and Evaluate Synchronization State<\/a>. Take it away, Rudolf&#8230;<b><\/b><\/p>\n<p>Yesterday, I described the Time Sync module and I explained how the main inline script works. Those of you who checked my source code may realize that I used special error handling for the whole inline script (I do not mean error handling inside of the inline script).<\/p>\n<p style=\"margin-left:30px\"><b>Note&nbsp;<\/b> Setting the operations discussed in today&rsquo;s post require elevated rights; otherwise, you will get an <b>Access is denied<\/b> error message.<\/p>\n<h2>Parallel operations<\/h2>\n<p>When you run parallel operations, for example, using <b>foreach &ndash;parallel {}<\/b>, or in my case, <b>using {} &ndash;PSComputerName $multipleServers<\/b>, it is not possible to simply enclose whole inline script in a <b>Try<\/b>\/<b>Catch<\/b> block because multiple exceptions may happen at the same time. You need to catch all the exceptions in a specified variable (<b>-PSError $myVar<\/b>), and then process them.<\/p>\n<p>If I run <b>Get-VSystemTimeSynchronization<\/b> against a local server or against a single remote server, I do not do the operations in parallel; and therefore, I also need to use a <b>Try<\/b>\/<b>Catch<\/b> block.<\/p>\n<p>Later, I want to process all <b>ErrorRecord<\/b> objects, and I do not care if the exception happened in standard or in parallel execution. This is the reason I save all errors to the same variable.<\/p>\n<p style=\"margin-left:30px\">try<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; InlineScript<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Some code<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; } -PSComputerName $ComputerName `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -PSPersist:$false `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -PSError $inlineScriptErrorParallelItems<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">catch [System.Management.Automation.Remoting.PSRemotingTransportException]<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $inlineScriptErrorItems = $_<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">if ($inlineScriptErrorParallelItems) { $inlineScriptErrorItems = $inlineScriptErrorParallelItems }<\/p>\n<p>Now I have all the exceptions in the same variable and I can process it. Objects from parallel execution are enclosed in another object; therefore, I need to take them out of the <b>Exception<\/b> property:<\/p>\n<p style=\"margin-left:30px\">foreach ($inlineScriptErrorFullItem in $inlineScriptErrorItems)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($inlineScriptErrorFullItem.PSObject.Properties.Name -eq &#039;Exception&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $inlineScriptErrorItem = $inlineScriptErrorFullItem.Exception<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $inlineScriptErrorItem = $inlineScriptErrorFullItem<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p>Then I continue with error handling. All objects are of the same type; and therefore, they have the same properties.<\/p>\n<h2>Ignoring certain errors<\/h2>\n<p>I implemented a feature that allows you to ignore wrong computer names or computers that are not accessible. If you use it, you can, for example, specify to be notified when the time synchronization does not work (the <b>Property<\/b> status of the output object is <b>$false<\/b>), but you do not care when the computer is inaccessible (for example, because of network failure or during a service window).<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # Ignore defined errors<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($IgnoreError -contains &#039;WrongComputerName&#039; -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ($inlineScriptErrorItem.ErrorRecord.CategoryInfo | Select-Object -First 1 -ExpandProperty Category) -eq &#039;ResourceUnavailable&#039; -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $inlineScriptErrorItem.TransportMessage -like &#039;The network path was not found.*&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Warning -Message &#039;Device does not exists (not in DNS).&#039;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; elseif ($IgnoreError -contains &#039;DeviceIsNotAccessible&#039; -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ($inlineScriptErrorItem.ErrorRecord.CategoryInfo | Select-Object -First 1 -ExpandProperty Category) -eq &#039;ResourceUnavailable&#039; -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $inlineScriptErrorItem.TransportMessage -like &#039;*Verify that the specified computer name is valid, that the computer is accessible over the network*&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;Write-Warning -Message &#039;Device exists (defined in DNS) but it is not reachable (not running, FW issue, etc.).&#039;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<h2>Logging errors<\/h2>\n<p>I wrote the main inline script so that any exceptions are captured and logged in the <b>ErrorEvents<\/b> property of the output object. But when there is a mistake, I want to terminate the script immediately (for example, when an unhandled exception occurs). I wrote the workflow with<b> $ErrorActionPreference = &lsquo;Stop&rsquo;<\/b> (all exceptions are terminating), so a simple <b>Write-Error<\/b> is enough to terminate the script.<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Terminating error<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error -Exception $inlineScriptErrorItem.ErrorRecord.Exception<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>That is all for the most important workflow, <b>Get-VSystemTimeSynchronization<\/b>. Let&lsquo;s continue with <b>Start-VSystemTimeSynchronization<\/b>.<\/p>\n<p><b>Start-VSystemTimeSynchronization<\/b> is a simple workflow to invoke time synchronization. As usual, I start with <b>ErrorActionPreference<\/b> and I do not want to see the progress bars:<\/p>\n<p style=\"margin-left:30px\">$ErrorActionPreference = &#039;Stop&#039;<\/p>\n<p style=\"margin-left:30px\">$ProgressPreference = &#039;SilentlyContinue&#039;<span style=\"font-size:12px\">&nbsp;<\/span><\/p>\n<h2>Start the Windows Time service<\/h2>\n<p>I need to start the W32Time (Windows Time) service to use the <b>w32tm<\/b> command:<\/p>\n<p style=\"margin-left:30px\">if ((Get-Service -Name W32Time).Status -ne &#039;Running&#039;)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Verbose -Message &#039;[Start] [Start service] [Verbose] Start service: W32Time (Windows Time)&#039;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Start-Service -Name W32Time<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<h2>Invoke time sync<\/h2>\n<p>Then I can invoke time synchronization. The most common command, <b>w32tm \/resync \/force<\/b>, triggers immediate time synchronization, and command <b>w32tm \/resync \/rediscover<\/b> rediscovers sources. Source rediscovery is important if the current source does not match the defined NTP server. This can happen, for example, when a computer cannot access the NTP server, and therefore, it uses only the internal clock. In this case, the current source is the internal clock, and that is not the desired state.<\/p>\n<p style=\"margin-left:30px\">if ($Rediscover)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $w32tmOutput = InlineScript { &amp; &#039;w32tm&#039; &#039;\/resync&#039;, &#039;\/rediscover&#039; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">elseif ($Force)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $w32tmOutput = InlineScript { &amp; &#039;w32tm&#039; &#039;\/resync&#039;, &#039;\/force&#039; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">else<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $w32tmOutput = InlineScript { &amp; &#039;w32tm&#039; &#039;\/resync&#039; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>At the end, it is handy to check if the command ran successfully and return a Boolean value that informs about it:<\/p>\n<p style=\"margin-left:30px\">if ($w32tmOutput | Select-String -Pattern &#039;The command completed successfully.&#039;)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Debug -Message (&#039;[Start] [Synchronization] [Debug] Command completed successfully.&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $true<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">else<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Warning -Message (&#039;[Start] [Synchronization] [Error] Command did not completed successfully.&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $false<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<h2>Wait for success<\/h2>\n<p>Another workflow in the module is <b>Wait-VSystemTimeSynchronization<\/b>. This workflow basically uses the <b>Get-VSystemTimeSynchronization<\/b> and <b>Start-VSystemTimeSynchronization<\/b> workflows. It starts the time sync if needed and waits for success.<\/p>\n<p>It is possible to limit the number of attempts to fix the time synchronization. 0 (default) means infinite attempts, &nbsp;1 means only one attempt, and so on.<\/p>\n<p style=\"margin-left:30px\">$repetitionCountCurrent = 1<\/p>\n<p style=\"margin-left:30px\">$status = $false<\/p>\n<p style=\"margin-left:30px\">while (!$status -and ($RepetitionCount -eq 0 -or $repetitionCountCurrent -le $RepetitionCount))<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($RepetitionCount -gt 0)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose -Message (&#039;[Wait] Repetition: {0} \/ {1}&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $repetitionCountCurrent, $RepetitionCount)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $repetitionCountCurrent++<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose -Message (&#039;[Wait] Repetition&#039;)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p>The current state is obtained from all the servers in parallel:<\/p>\n<p style=\"margin-left:30px\">$outputItems = Get-VSystemTimeSynchronization `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -ComputerName $ComputerName `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -RequiredSourceName $RequiredSourceName `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -RequiredSourceTypeConfiguredInRegistry $RequiredSourceTypeConfiguredInRegistry `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -RequiredSourceTypeNotLocal $RequiredSourceTypeNotLocal `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -RequiredSourceTypeNotByHost $RequiredSourceTypeNotByHost `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -LastTimeSynchronizationMaximumNumberOfSeconds $LastTimeSynchronizationMaximumNumberOfSeconds `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -CompareWithNTPServerName $CompareWithNTPServerName `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -CompareWithNTPServerMaximumTimeDifferenceSeconds $CompareWithNTPServerMaximumTimeDifferenceSeconds `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -IgnoreError $IgnoreError `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Verbose:$false `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -PSPersist:$false<\/p>\n<h2>Produce a report<\/h2>\n<p>Now let&rsquo;s count the number of successful and unsuccessful results (custom objects from all servers). All unsuccessful results are divided into groups. One group is servers with the wrong source and another group is servers with other issues.<\/p>\n<p style=\"margin-left:30px\">$outputOKItems = $outputItems |<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Where-Object -FilterScript { $_.Status -eq $true }<\/p>\n<p style=\"margin-left:30px\">$outputWrongSourceItems = $outputItems |<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Where-Object -FilterScript { $_.StatusSourceName -eq $false -or $_.StatusSourceType -eq $false }<\/p>\n<p style=\"margin-left:30px\">$outputOtherErrorItems = $outputItems |<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Where-Object -FilterScript { $_.Status -ne $true -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; ($_.StatusSourceName -ne $false -or $_.StatusSourceType -ne $false) }<\/p>\n<p>Now let&rsquo;s do the corrective actions. If the server is in group with the wrong source, the corrective action is to rediscover the source. Other servers are only forced to run immediate synchronization.<\/p>\n<p style=\"margin-left:30px\">if ($CorrectiveActions -and ($outputWrongSourceItems -or $outputOtherErrorItems))<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($outputWrongSourceItems)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose -Message (&#039;[Wait] Correction action: Rediscover ({0}): {1}&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @($outputWrongSourceItem).Count, ($outputWrongSourceItem.ComputerNameNetBIOS -join &#039;, &#039;))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = Start-VSystemTimeSynchronization `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Rediscover:$true `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -PSComputerName $outputWrongSourceItem.ComputerNameNetBIOS<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($outputOtherErrorItems)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose -Message (&#039;[Wait] Correction action: Immediate synchronization ({0}): {1}&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @($outputOtherErrorItems).Count, ($outputOtherErrorItems.ComputerNameNetBIOS -join &#039;, &#039;))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = Start-VSystemTimeSynchronization `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Force:$true `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -PSComputerName $outputWrongSourceItem.ComputerNameNetBIOS<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">else<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $status = $true<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>Now we wait a specified number of seconds before another attempt. If the current attempt is the last one, there is no reason to wait anymore.<\/p>\n<p style=\"margin-left:30px\">if ($RepetitionDelaySeconds -gt 0 -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; ($RepetitionCount -eq 0 -or $repetitionCountCurrent -le $RepetitionCount))<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Debug -Message (&#039;[Wait] [Debug] Delay: {0} seconds&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $RepetitionDelaySeconds)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Start-Sleep -Seconds $RepetitionDelaySeconds<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>At the end, all objects from <b>Get-VSystemTimeSynchronization<\/b> are returned, regardless of the corrective action results:<\/p>\n<p style=\"margin-left:30px\">if ($outputItems)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if (@($outputItems).Count -eq @($computerNameItems).Count)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Verbose -Message (&#039;[Wait] [Verbose] Finish: {0} devices&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @($outputItems).Count)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; else<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Warning -Message (&#039;[Wait] [Error] Not all devices were queried: {0} \/ {1}&#039; -f<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @($outputItems).Count, @($computerNameItems).Count)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $outputItems<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p style=\"margin-left:30px\">else<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Write-Warning -Message (&#039;[Wait] [Error] No data&#039;)<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>The output from the commands is shown in the following image:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-5-28-15-1.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-5-28-15-1.jpg\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>That is all for today. In the next and last post, I will explain the last two workflows from the Time Sync module.<\/p>\n<p>~Rudolf<\/p>\n<p>Thank you, Rudolf. Please join us tomorrow for the final post in this series.&nbsp;<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><span style=\"font-size:12px\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Rudolf Vesely talks about error handling in workflows and repair actions. Microsoft Scripting Guy, Ed Wilson, is here. Today is Part 2 of a 3-part series written by guest blogger Rudolf Vesely. Read yesterday&rsquo;s post to catch up and to learn more about Rudolf: PowerShell Time Sync: Get and Evaluate Synchronization State. [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,584,3,585,586,45,382],"class_list":["post-5891","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-rudolf-vesely","tag-scripting-guy","tag-time-sync","tag-time-synchronization","tag-windows-powershell","tag-workflow"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Rudolf Vesely talks about error handling in workflows and repair actions. Microsoft Scripting Guy, Ed Wilson, is here. Today is Part 2 of a 3-part series written by guest blogger Rudolf Vesely. Read yesterday&rsquo;s post to catch up and to learn more about Rudolf: PowerShell Time Sync: Get and Evaluate Synchronization State. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/5891","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=5891"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/5891\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=5891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=5891"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=5891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}