<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Mitch Klann's Blog - Mostly Automated</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/mitchk/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/mitchk/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:47:58 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Write-Log Function with Context</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/write-log-function-with-context</link>
      <pubDate>Mon, 08 May 2017 07:59:47 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/?p=345</guid>
      <description><![CDATA[Along the same lines as my previous post, I wanted to share the latest version of my logging...]]></description>
      <content:encoded><![CDATA[Along the same lines as my previous post, I wanted to share the latest version of my logging function.  I find this function particularly useful when writing verbose log entries that need to be searched in an automated fashion.  It’s a good practice to implement full tracking of every automated action such as a server reboot, user modifications, etc.  This function will output the data to a file which can be read in as a CSV and converted into objects.  These logging entries can then be searched for without having to manually search text files.
<div id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:4f5921f0-0ced-4b1f-b54b-fa0eddca102b" class="wlWriterEditableSmartContent" style="width: 1092px;float: none;margin-left: auto;margin-right: auto;padding: 0px">
<pre>function Write-Log {
    param(
        [string]$Message,
        [string]$EntryType = "Info",
        [string]$ObjectContext,
        [string]$CodeContext,
        [string]$Details,
        $LogName = "output.log",
        $LogPath
    )
    try {
        if ([string]::IsNullOrEmpty($LogPath)) {
            $LogPath = $pwd.path
        }
        if (!(Test-Path $LogPath\$LogName -ErrorAction SilentlyContinue)) {
            "Timestamp,EntryType,Message,ObjectContext,CodeContext,Details" | Out-File -FilePath $LogPath\$LogName
        }
        foreach ($paramInfo in $MyInvocation.BoundParameters.Keys) {
            #If parameter passed in contains a comma, wrap the entry in quotes to not break CSV delimiters.
            $parameterData = $MyInvocation.BoundParameters[$paramInfo].ToString()
            if ($parameterData.Contains(",")) {
                Set-Variable "$paramInfo" -Value $parameterData.Replace(
                    "$parameterData","`"$parameterData`"")
            }
        }
        "$([DateTime]::Now.ToString("MM-dd HH:mm:ss")),$EntryType,$Message,$ObjectContext,$CodeContext,$Details".Replace("\n",";") | `
            Out-File -Append -FilePath "$LogPath\$LogName"
    }
    catch {
        Write-Warning "Unexpected error occured while executing $((Get-PSCallStack)[0].Command) with exception: $($_.Exception)"
        Write-Warning "Command: `'$($_.InvocationInfo.Line.Trim())`'"
    }
}

</pre>
</div>
The overall concept is we have a few key properties that make up the log type.  We will of course need a <em>Message</em> property, as well as a <em>Timestamp</em> when the message was written.  Key to large scale scripts is an <em>ObjectContext</em> and a <em>CodeContext</em> which store contextual information about executing code and objects that code is operating against.  The intent of the ObjectContext property should be a string representation of the object you are making a log entry about.  Having an object context makes searching the logs easy for something that happened to a particular user account for example – just load the log into memory and execute a search against ObjectContext and you can see every log entry against that user account.  Once these entries are logged, it’s easy to read them back into your PowerShell session and execute queries.

Writing to the log is simple – the function will automatically create the file and initialize header information if the file did not exist.  It will also automatically encapsulate log entries with quotes if the value contains a comma which is our CSV delimiter.

<a href="https://msdnshared.blob.core.windows.net/media/2017/05/WriteToLog.png"><img title="WriteToLog" style="float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px" border="0" alt="WriteToLog" src="https://msdnshared.blob.core.windows.net/media/2017/05/WriteToLog_thumb.png" width="964" height="72" /></a>

Here are a couple examples of searching the log data we just created.  First, we want to see any log entries that mention the w32time service as the ObjectContext.  In the results, we can see the service starting and stopping.  The next example will search for Messages of a certain type, in this case any time a service was stopped.  Alternatively if we were logging CodeContext, we could look for a function name or something similar.

<a href="https://msdnshared.blob.core.windows.net/media/2017/05/ReadFromLog.png"><img title="ReadFromLog" style="float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px" border="0" alt="ReadFromLog" src="https://msdnshared.blob.core.windows.net/media/2017/05/ReadFromLog_thumb.png" width="964" height="212" /></a>

This may not be the best logging solution for every scenario, but it works especially well when there are hundreds or thousands of log events being created in one file.]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Error Output</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/powershell-error-output</link>
      <pubDate>Thu, 09 Mar 2017 17:56:11 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/?p=295</guid>
      <description><![CDATA[I’ve implemented some crazy ways of error handling over the years.&nbsp; I wanted to quickly show...]]></description>
      <content:encoded><![CDATA[<p>I’ve implemented some crazy ways of error handling over the years.&nbsp; I wanted to quickly show and explain what I <strong>currently</strong> put in my Catch block for most of my PowerShell functions.&nbsp; Here is a typical catch statement in one of my functions:  <blockquote> <p>Write-Warning "Unexpected error occurred while executing $((Get-PSCallStack)[0].Command) with exception: $($_.Exception.Message)"  <p>Write-Warning "Command: `'$($_.InvocationInfo.Line.Trim())`'"</p></blockquote> <p>I’ll usually have some error handling for specific conditions such as unexpected object state/data which usually results in a return from the function, so if we actually hit a catch block, something is REALLY not right.&nbsp; If you have a script with several hundred lines, a dozen functions and you suddenly hit an unexpected error, it can be tedious to figure out where the error actually came from.&nbsp; In the past, I’ve gone back to my scripts, added a bunch of ‘Write-Host “Just passed this section of code”’ or similar debug lines.&nbsp; I very rarely have to perform this level of debugging with my current catch block reporting.  <p>Let’s break down what is happening here and take a look at some sample output.&nbsp; Get-PSCallStack was designed to be used with the PowerShell debugger. It works inside scripts and functions.&nbsp; Here we are looking at the first item in the call stack, getting the command and displaying the exception message.&nbsp; This is the function name, or script name if you’re not in a function.&nbsp; The second line takes a look at the InvocationInfo property of our exception. The InvocationInfo has a property called Line which contains the text from the line of the script or function that generated the error. Another option in this scenario instead of collecting the Line property is to collect the MyCommand.Name property which will return the cmdlet that generated the error. Given the context of the caller and the line or name of the cmdlet, it will be easier to determine where the error is occurring.  <p>Seen below, we have some sample information from a function called Get-FileInformation, which is throwing an error when calling the Test-Path cmdlet with a null value.  <p><a href="https://msdnshared.blob.core.windows.net/media/2017/03/GetFileInfoReport.png"><img title="GetFileInfoReport" style="border-top: 0px;border-right: 0px;border-bottom: 0px;float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;border-left: 0px;padding-right: 0px;margin-right: auto" border="0" alt="GetFileInfoReport" src="https://msdnshared.blob.core.windows.net/media/2017/03/GetFileInfoReport_thumb.png" width="1080" height="296"></a> <p>In the output, we have a lot more information about what caused the error, and where we should start looking for the problem.</p>]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Prompts – Part 2</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/powershell-prompts-part-2</link>
      <pubDate>Tue, 22 Dec 2015 14:39:08 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2015/12/22/powershell-prompts-part-2/</guid>
      <description><![CDATA[In Part 1 of this series, I talked about an Approve-Action function.&nbsp; This is great for a...]]></description>
      <content:encoded><![CDATA[<p>In <a href="http://blogs.technet.com/b/mitchk/archive/2015/08/06/powershell-prompts-part-1.aspx" target="_blank">Part 1</a> of this series, I talked about an Approve-Action function.&nbsp; This is great for a single go/no-go approval.&nbsp; Unfortunately not all decisions are so boolean.&nbsp; For more advanced problems, I developed the Confirm-UserInput function, which instead focuses on validation input versus action approval.&nbsp; The purpose of this function is to enforce user input to conform to a list of available options, with the objective being less code overall for the script author.&nbsp; Take for example the following scenario:</p> <p>I want to know which color you want to make your background.&nbsp; Let’s say my script respects only red, green and blue.&nbsp; You could have some code looking similar to the following:</p> <blockquote> <p>$userColor = read-host “Enter the color (red, green or blue)”</p> <p>if ($userColor –eq “red”) {</p> <p>}</p> <p>if ($userColor –eq “green”) {</p> <p>}…etc</p></blockquote> <p>Another option would be:</p> <blockquote> <p>if ($userColor –match “red|green|blue”) {</p> <p>$Background.Color = $userColor</p> <p>}</p> <p>else {</p> <p>#error or pick default color</p> <p>}</p></blockquote> <p>In either scenario, if the user provides bad input you can either default to some color or you would be forced to set up a loop to prompt the user again.&nbsp; When I wrote this function, my goal was to remove a bit of the headache for the script author on which code path could possibly taken depending on who is running the console.</p> <p>Here is the function, and example for our background color scenario:</p> <p>function Confirm-UserInput {<br />&nbsp;&nbsp;&nbsp; param(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Message = &quot;Enter one of the following choices.&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [string[]]$Choices = @(&quot;&amp;Yes&quot;,&quot;&amp;No&quot;,&quot;&amp;Cancel&quot;)<br />&nbsp;&nbsp;&nbsp; )<br />&nbsp;&nbsp;&nbsp; $ListChoices = @()<br />&nbsp;&nbsp;&nbsp; foreach ($Choice in $Choices) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ListChoices += [System.Management.Automation.Host.ChoiceDescription]$Choice<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; return $Choices[$Host.UI.PromptForChoice(`<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Confirm&quot;,$Message,$ListChoices,0)].Replace(&quot;&amp;&quot;,&quot;&quot;)<br />}</p> <p>$Background.Color = (Confirm-UserInput –Message “Choose a valid color.” –Choices “&amp;Blue”,”&amp;Green”,”&amp;Red”)</p> <p>Once this command executes, I know I can immediately assign the return to the Color property, as the return has to be one of the choices I defined.</p> <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/ConfirmUserInput-1_156F80D9.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/ConfirmUserInput_2D00_1_5F00_156F80D9.png"><img title="ConfirmUserInput-1" style="border-left-width:0px;border-right-width:0px;background-image:none;border-bottom-width:0px;padding-top:0px;padding-left:0px;display:inline;padding-right:0px;border-top-width:0px;" border="0" alt="ConfirmUserInput-1" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/ConfirmUserInput-1_thumb_452D2958.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/ConfirmUserInput_2D00_1_5F00_thumb_5F00_452D2958.png" width="644" height="89" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Prompts – Part 1</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/powershell-prompts-part-1</link>
      <pubDate>Thu, 06 Aug 2015 06:18:00 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2015/08/06/powershell-prompts-part-1/</guid>
      <description><![CDATA[I always get nervous when I am writing scripts that could negatively impact users consuming a...]]></description>
      <content:encoded><![CDATA[<p>I always get nervous when I am writing scripts that could negatively impact users consuming a service, such as rebooting a server or restarting critical services.&nbsp; I do my best to make sure the person running the script is aware of what is about to happen.&nbsp; I feel the best when I can make sure the administrator has complete control and force them to confirm an action the script is about to execute.&nbsp; In the past, I’ve implemented a very basic form of prompting for approval, here is an example from one of my older scripts:</p> <blockquote> <p>$ConfirmPrompt = Read-Host &quot;Are you sure you want to SHUT DOWN the above servers in the listed site(s)?&nbsp; Type &#39;Yes&#39; to continue&quot; </p> <p>if ($ConfirmPrompt -ne &#39;Yes&#39;) {<br />&nbsp;&nbsp;&nbsp; Write-Warning &quot;Confirm failed.&nbsp; Script will quit.&quot;<br />&nbsp;&nbsp;&nbsp; Exit<br />}</p></blockquote> <p>Obviously in this case the administrator has to explicitly type “Yes” in the prompt or the script will exit.&nbsp; This is not very reliable.&nbsp; What happens if the user wants to type in “Y” or does not spell the keyword properly?&nbsp; The script would exit and the user would have to re-run the entire script because of a mistake.&nbsp; In the first part of this two part post, I want to show a function I wrote to approve a yes/no command like this one.&nbsp; To me, it feels a bit more ‘native PowerShell’, without having to build your own loop.</p> <blockquote> <p>Function Approve-Action {<br />&nbsp;&nbsp;&nbsp; param(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Message = &quot;Are you sure you want to perform this action?&quot;<br />&nbsp;&nbsp;&nbsp; )<br />&nbsp;&nbsp;&nbsp; [string[]]$Choices = @(&quot;&amp;Yes&quot;,&quot;&amp;No&quot;)<br />&nbsp;&nbsp;&nbsp; $ListChoices = @()<br />&nbsp;&nbsp;&nbsp; foreach ($Choice in $Choices) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ListChoices += [System.Management.Automation.Host.ChoiceDescription]$Choice<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; $Results = $Host.ui.PromptForChoice(&quot;Confirm&quot;, $Message, $ListChoices, 0)<br />&nbsp;&nbsp;&nbsp; return ($Choices[$Results] -match &quot;Yes&quot;)<br />}<br /></p></blockquote> <p>This function uses PowerShell’s built-in PromptForChoice method, which has it’s own input validation mechanism.&nbsp; If the user types “Cancel” – the validation realizes this is not a valid option and will prompt the user again until a valid option is selected.&nbsp; Worth mentioning is the “&amp;” at the beginning of each choice input will allow the user to type the following letter to select that choice:</p> <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/PowerShellConfirmPrompt1_0D7D11CE.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/PowerShellConfirmPrompt1_5F00_0D7D11CE.png"><img title="PowerShellConfirmPrompt1" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="PowerShellConfirmPrompt1" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/PowerShellConfirmPrompt1_thumb_36AC5C1F.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/PowerShellConfirmPrompt1_5F00_thumb_5F00_36AC5C1F.png" width="454" height="70" /></a></p> <p>The implementation is simple:</p> <blockquote> <p>if (Approve-Action “OK to reboot this computer?”) {<br />&nbsp;&nbsp;&nbsp; Restart-Computer<br />}</p></blockquote> <p>In part 2, I will cover a different implementation showing custom choices instead of a simple yes/no.</p>]]></content:encoded>
    </item>
    <item>
      <title>Log-Event</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/log-event</link>
      <pubDate>Wed, 31 Dec 2014 11:48:11 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/12/31/log-event/</guid>
      <description><![CDATA[Recently I had a need to write to the event logs using PowerShell.&nbsp; I knew there was a built in...]]></description>
      <content:encoded><![CDATA[<p>Recently I had a need to write to the event logs using PowerShell.&nbsp; I knew there was a built in command for this action so I decided to test it out.&nbsp; After some time using the Write-EventLog cmdlet, I kept getting errors generated which seemed to be symptoms from the following constraints of the event log itself.&nbsp; When using the cmdlet, the following apply:</p> <p>-You must specify a source and a log (Application, System, etc) to write to.&nbsp; An example of a source would be “Windows Error Reporting” in the Application log.</p> <p>- A source can only be associated with one event log.&nbsp; If you attempt to write an event using the source “Windows Error Reporting” to the System log, you will get the following error: The source &#39;Windows Error Reporting&#39; is not registered in log &#39;System&#39;. (It is registered in log &#39;Application&#39;.) </p> <p>Since the Write-EventLog cmdlet makes you specify the log to write to with the mandatory –LogName parameter, this can be problematic because you have to know which log the source is associated with.&nbsp; From a programmatic perspective, you can’t guarantee all computers everywhere have the source “AutomationLogs” registered, and you also can’t guarantee which event log this is registered to.</p> <p>I developed the following function to assist me in writing to the event logs that I plan to implement in a lot of my scripts moving forward.&nbsp; I wanted to be able to call a command and specify what I want to write and not have to worry about sources, log locations, registering new event logs if needed, and so on.</p> <p>The function first checks to see if the source is already registered with an existing log.&nbsp; If the source exists, the function leverages the static method LogNameFromSourceName from the System.Diagnostics.EventLog class to determine which log to write to.&nbsp; If the return from this function is null or an empty string, we know the source does not exist and will have to register a new event source to the desired log (as passed in by the caller).&nbsp; </p> <p>This function requires administrative access in an elevated command prompt, because it needs to check the Security log, and this access is also required when registering new sources.</p> <p>According to the MSDN documentation regarding creation of <a href="http://msdn.microsoft.com/en-us/library/2awhba7a(v=vs.110).aspx">event sources</a> – after registering a new event source, it should not be immediately used.&nbsp; I am not sure exactly how long we should wait but I assumed 5 seconds is ample time for whatever replication/post-processing tasks need to happen on the back end before we use this.</p> <p>I have tested this on Server 2012 and 2012 R2, but any machine with PowerShell 3 or later should be able to leverage the function.&nbsp; The function is below and can also be found on the TechNet Script Gallery <a href="https://gallery.technet.microsoft.com/scriptcenter/Log-Event-Function-0cb0015c" target="_blank">here</a>.</p> <p>&nbsp;</p> <blockquote> <p>function Log-Event {<br />&nbsp;&nbsp;&nbsp; param(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Mandatory=$true)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ValidateNotNullorEmpty()]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String]$Message,</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ValidateNotNullorEmpty()]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String]$Source = &quot;ScriptOutput&quot;,</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [int32]$EventID = 1,</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $LogName = &quot;Application&quot;,</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [System.Diagnostics.EventLogEntryType]$Type = &quot;Information&quot;<br />&nbsp;&nbsp;&nbsp; )<br />&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $SourceLogName = [System.Diagnostics.EventLog]::LogNameFromSourceName($source, &quot;.&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (![string]::IsNullOrEmpty($SourceLogName)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-EventLog -LogName $SourceLogName -Source $Source -Message $Message -EntryType $Type -EventId $EventID<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; New-EventLog -Source $Source -LogName $LogName<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #After register new event source, do not use right away, accoring to MSDN:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #<a href="http://msdn.microsoft.com/en-us/library/2awhba7a(v=vs.110).aspx">http://msdn.microsoft.com/en-us/library/2awhba7a(v=vs.110).aspx</a><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep 5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-EventLog -LogName $LogName -Source $Source -Message $Message -EntryType $Type -EventId $EventID<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; catch {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Warning &quot;Unexpected error occured while attempting to write to log.&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Warning &quot;Caller command: $((Get-PSCallStack)[1].Command)&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Warning &quot;Error: $($_.exception.message)&quot;<br />&nbsp;&nbsp;&nbsp; }<br />}</p></blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Shutting Down an Exchange Site</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/shutting-down-an-exchange-site</link>
      <pubDate>Sun, 31 Aug 2014 21:03:00 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/08/31/shutting-down-an-exchange-site/</guid>
      <description><![CDATA[The argument could be made that this script is not something that has a practical use.&nbsp; One of...]]></description>
      <content:encoded><![CDATA[<p>The argument could be made that this script is not something that has a practical use.&nbsp; One of my customers had a requirement to quickly (and gracefully) shut down an entire site of Exchange servers.&nbsp; I assume the scenario would be due to some kind of natural disaster or cooling issue in a datacenter.&nbsp; I am posting this because one of my customers had an actual need for it, and some of the techniques in this script could be applied to other scripts.&nbsp; I will leave the practicality of this script up to the reader.</p>
<p>The script will identify servers by site and optionally by server name as well.&nbsp; The script leverages WinRM to stop services remotely on multiple servers, so WinRM must be enabled and firewall rules must be opened.&nbsp; The advantage is this can be executed on any number of servers and should take generally around the same time to complete.</p>
<p>After the script identifies which servers need to be shut down, it will dismount any databases that are mounted and stop the cluster service on DAG nodes, as well as stopping the msExchangeADTopology service (and dependent services).&nbsp; Once all services are stopped, the script will initiate a shutdown of each server.</p>
<p>Here is the script in action:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/Stop-ExchangeSite3_2E905C63.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/Stop_2D00_ExchangeSite3_5F00_2E905C63.png"><img width="644" height="445" title="Stop-ExchangeSite3" style="background-image:none;padding-top:0px;padding-left:0px;display:inline;padding-right:0px;border-width:0px;" alt="Stop-ExchangeSite3" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/Stop-ExchangeSite3_thumb_7DFCDEAA.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/Stop_2D00_ExchangeSite3_5F00_thumb_5F00_7DFCDEAA.png" border="0" /></a></p>
<p>As a reminder, all servers will need to be manually powered back on; in addition, all databases that were dismounted will need to be mounted after the servers are brought back online.</p>
<p></p>
<p>*Edit* - The script can be found <a title="here" href="http://gallery.technet.microsoft.com/scriptcenter/Shutdown-Exchange-6a2c1a59">here</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Caching Objects in PowerShell – Part 2</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/caching-objects-in-powershell-part-2</link>
      <pubDate>Thu, 31 Jul 2014 19:46:38 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/07/31/caching-objects-in-powershell-part-2/</guid>
      <description><![CDATA[In my previous post about this subject, I demonstrated how to cache objects using an XML file. Let’s...]]></description>
      <content:encoded><![CDATA[<p>In my <a href="http://blogs.technet.com/b/mitchk/archive/2014/06/30/caching-objects-in-powershell-part-1.aspx">previous post</a> about this subject, I demonstrated how to cache objects using an XML file. Let’s take a look at how to load, manipulate and save the data again.</p> <p>Since the Export-Clixml cmdlet retains the data type in the XML file, we can perform actions on the objects in memory that are type-specific without having to cast the objects.&nbsp; This means if we have an integer value, we can increment the count by calling the ++ operator.&nbsp; Likewise, if we have a DateTime object, we can call type-specific methods such as .ToUniversalTime().&nbsp; I think this is very cool and I wish I’d discovered this sooner.&nbsp; </p> <p>Loading the XML back into memory couldn’t be easier:</p> <blockquote> <p>$DBCache = Import-Clixml C:\DatabaseCache.xml</p></blockquote> <p>Using the data created in part 1, let’s assume we just created a new mailbox on Database-1.&nbsp; With the below code, we can easily take the objects in memory and increment the count to show the new updated mailbox number.&nbsp; Another example would be adding time to the Timestamp attribute before we save .</p> <blockquote> <p>($DBCache | where {$_.DatabaseName –eq “Database-1”}).UserCount++</p> <p>foreach ($DBItem in $DBCache) { $DBItem.Timestamp.AddHours(3) }</p></blockquote> <p>Once the objects are modified, simply overwrite the XML file with the updated data:</p> <blockquote> <p>$DBCache | Export-Clixml “C:\DatabaseCache.xml”</p></blockquote> <p>I realize this is a very simple example but the principal applies to any class and any of those class methods.</p>]]></content:encoded>
    </item>
    <item>
      <title>Caching Objects in PowerShell – Part 1</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/caching-objects-in-powershell-part-1</link>
      <pubDate>Mon, 30 Jun 2014 19:27:00 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/06/30/caching-objects-in-powershell-part-1/</guid>
      <description><![CDATA[In the first section of this two part series, I will discuss caching objects to disk in PowerShell,...]]></description>
      <content:encoded><![CDATA[<p>In the first section of this two part series, I will discuss caching objects to disk in PowerShell, and reading the cache back into memory.&nbsp; I recently came up with this solution at a customer site where I was running a script as a scheduled task and needed some data to persist after the task was complete.&nbsp; I use this to track different errors and how many times I&rsquo;ve encountered those errors; as well as caching data from complex (read: expensive) LDAP queries.</p>
<p>So let&rsquo;s take a look at something LDAP we could cache.&nbsp; Let&rsquo;s say I have an array of objects in memory representing Exchange databases with current number of mailboxes (I like to query LDAP for this data - see <a href="/b/mitchk/archive/2014/01/16/collecting-mailbox-counts-per-database-using-ldap.aspx">this post</a>).&nbsp; The objects could be anything &ndash; but the concept is straightforward as we will cache the data to disk using XML.&nbsp; For this example, let&rsquo;s create some fake data with custom PSObjects.&nbsp; I chose to use different types (string, int, datetime) on purpose here to show how this really shines compared to using something like export/import &ndash;csv, which only recognizes strings without doing type casting or conversion.</p>
<blockquote>
<p>$DBs = @()<br />$DBs += New-Object psobject -Property @{DatabaseName=&quot;Database-1&quot;; UserCount = 3800; Timestamp=(Get-Date)}<br />$DBs += New-Object psobject -Property @{DatabaseName=&quot;Database-2&quot;; UserCount = 2600; Timestamp=(Get-Date)}</p>
</blockquote>
<p>Once the array of custom objects is created, we can simply export it using the built in Export-Clixml cmdlet.</p>
<blockquote>
<p>$DBs | Export-Clixml .\DatabaseCache.xml</p>
</blockquote>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/SimulateData_25A5756B.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/SimulateData_5F00_25A5756B.png"><img width="579" height="111" title="CreateFakeData" style="float:none;margin-left:auto;display:block;margin-right:auto;border-width:0px;" alt="CreateFakeData" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/SimulateData_thumb_20C2C1AF.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/SimulateData_5F00_thumb_5F00_20C2C1AF.png" border="0" /></a></p>
<p>This will save all of our data to an XML file and retain associated type information, etc.&nbsp; Here is what the raw XML data looks like.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CacheDatabaseXML_3B8EE7BB.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CacheDatabaseXML_5F00_3B8EE7BB.png"><img width="307" height="399" title="DatabaseCacheXML" style="float:none;margin-left:auto;display:block;margin-right:auto;border-width:0px;" alt="DatabaseCacheXML" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CacheDatabaseXML_thumb_121AA67B.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CacheDatabaseXML_5F00_thumb_5F00_121AA67B.png" border="0" /></a></p>
<p>To use this data again, simply use the Import-Clixml cmdlet and you will have the exact same array of objects you previously exported.&nbsp; Stay tuned as my next post will show some examples of how to update the cache.</p>]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Performance: Write-Host</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/powershell-performance-write-host</link>
      <pubDate>Mon, 31 Mar 2014 08:40:45 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/03/31/powershell-performance-write-host/</guid>
      <description><![CDATA[&nbsp; This month’s post will be a short one – and there’s not much code to this topic, it’s just...]]></description>
      <content:encoded><![CDATA[<h5></h5> <h3>&nbsp;</h3> <p>This month’s post will be a short one – and there’s not much code to this topic, it’s just an observation.&nbsp; When I write scripts, I like the idea of adding some output to track what my script is doing, especially if there’s an issue with the script.&nbsp; It’s so much easier to find bugs or performance issues if I can see the last task that completed successfully – I then know where to start looking for the issue in my code.&nbsp; However, it is important to understand as more output is added, your script will slow down.&nbsp; It may not seem like much but when you are calling write-host multiple times for thousands of objects, overall script performance will be impacted.</p> <h3>&nbsp;</h3> <p>Let’s take an example script I wrote and assume the script will call a function in a foreach loop processing thousands of objects.&nbsp; I’ll also add a write-host in my foreach loop each time I enter and exit a function.&nbsp; That’s a lot of text but it could be meaningful if there is an issue.&nbsp; Let’s run the following code:</p> <blockquote> <p>function foo {<br />&nbsp;&nbsp;&nbsp; Write-Host &quot;Entering function &#39;foo&#39;&quot;<br />&nbsp;&nbsp;&nbsp; # Some code<br />&nbsp;&nbsp;&nbsp; Write-Host &quot;Completed function &#39;foo&#39;&quot;<br />}  <p>$startTime = Get-Date<br />write-host &quot;script started at $starttime&quot;<br />$Cycles = 10000</p> <p>1..$Cycles | % {<br />&nbsp;&nbsp;&nbsp; foo<br />}<br />Write-Host &quot;Done&quot;  <p>$endTime = Get-Date<br />($endTime - $startTime).totalseconds</p></p></p></blockquote> <p>This takes about 10 seconds to execute on my computer, which is significant since the only thing running is the write-host command.&nbsp; After commenting out the write-host commands, the script completes in about 1 second.&nbsp; In my personal experience, I have seen scripts where over 30 minutes have been saved by removing write-host output. </p> <p>My suggestion and what I do for most of my scripts: as an alternative to write-host, you can use write-verbose for output, and when you need to see what your script is doing you can call the script with the –verbose parameter.&nbsp; Your script will execute faster under normal circumstances, and you will still be able to output diagnostic information when you need it - with no code changes.</p>]]></content:encoded>
    </item>
    <item>
      <title>Estimating/Calculating Execution Time</title>
      <link>https://docs.microsoft.com/archive/blogs/mitchk/estimatingcalculating-execution-time</link>
      <pubDate>Thu, 27 Feb 2014 21:10:37 GMT</pubDate>
      <dc:creator><![CDATA[mitch-k]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/mitchk/2014/02/27/estimatingcalculating-execution-time/</guid>
      <description><![CDATA[Over the years, I&rsquo;ve noticed that I develop a general routine depending on the needs of my...]]></description>
      <content:encoded><![CDATA[<p>Over the years, I&rsquo;ve noticed that I develop a general routine depending on the needs of my current customer.&nbsp; When I get to work in the morning I generally check a few items that I know can develop into hot button issues.&nbsp; As a general rule, if I am going to repeat a task more than a few times, I write a quick script in PowerShell if possible.&nbsp; Lately I&rsquo;ve had to search for and modify several tens of thousands of objects in AD.&nbsp; The majority of my scripts display a progress bar.&nbsp; The progress bar is nice, but I wanted to improve the progress bar by adding an active countdown timer.</p>
<p>When I set out to write this, I wanted a function I can just drop into a script if I feel it&rsquo;s necessary, with minimal code changes.&nbsp; The function I came up with makes the following assumptions:</p>
<ul>
<li>Each processing task takes <em>relatively</em> the same amount of time to execute</li>
<li>You <strong>must</strong> know how many items you are processing.&nbsp; I&rsquo;m not sure mathematically how to solve this without knowing both how many items we have looped through and how many more we have left to process</li>
<li>If one or several of the processing tasks takes significantly longer than the other processing tasks (such as a network timeout), the estimated time remaining will be skewed</li>
</ul>
<p>After adding the function I created to a script, all I need to add is this code to my foreach loop (where $cycles is an integer representing the total objects to process):</p>
<blockquote>
<p>$i++<br />ShowExecutionTime -TotalObjectsToProcess $Cycles</p>
</blockquote>
<p>Here is the script in action:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CalculateRemainingExecutionTimeRunning_1BBF7B3A.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CalculateRemainingExecutionTimeRunning_5F00_1BBF7B3A.png"><img width="844" height="116" title="CalculateRemainingExecutionTimeRunning" style="display:inline;border-width:0px;" alt="CalculateRemainingExecutionTimeRunning" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CalculateRemainingExecutionTimeRunning_thumb_16DCC77E.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CalculateRemainingExecutionTimeRunning_5F00_thumb_5F00_16DCC77E.png" border="0" /></a></p>
<p>The script in the repository also has a &ndash;ShowCalculatedTimes parameter which will document each time the function estimates the script completion time, then will display how many times we guessed each specific completion time.&nbsp; Below are the results of the script with &ndash;ShowCalculatedTimes enabled:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CalculateRemainingExecutionTimeResults_52302D3C.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CalculateRemainingExecutionTimeResults_5F00_52302D3C.png"><img width="847" height="213" title="CalculateRemainingExecutionTimeResults" style="display:inline;border-width:0px;" alt="CalculateRemainingExecutionTimeResults" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/01/43/metablogapi/CalculateRemainingExecutionTimeResults_thumb_0D176006.png" original-url="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-01-43-metablogapi/CalculateRemainingExecutionTimeResults_5F00_thumb_5F00_0D176006.png" border="0" /></a></p>
<p>The sample script containing the function can be found <a href="http://gallery.technet.microsoft.com/Calculate-Remaining-cd2b067e" target="_blank">here</a>.</p>]]></content:encoded>
    </item>
  </channel>
</rss>