Jekyll2018-08-05T11:00:37+00:00https://www.ohadsoft.com/OhadSoft - Software with StyleSoftware with StyleOhad SchneiderAutomatically renewing Azure Web App HTTPS certificates for free using letsencrypt-webapp-renewer2017-09-09T00:00:00+00:002017-09-09T00:00:00+00:00https://www.ohadsoft.com/2017/09/automatically-renewing-azure-web-app-Https-certs-for-free-using-letsencrypt-webapp-renewer<p>HTTPS is the pervasive standard for all websites, regardless of size or field.
The Mozilla foundation has gone so far as to <a href="https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/">announce their intent to completely phase out HTTP</a>.
Unfortunately, the procurement, maintenance, and renewal of SSL/TLS certificates has been an expensive and manual process for many.</p>
<p>Enter <a href="https://letsencrypt.org/">Let’s Encrypt</a> - a free, automated, and open Certificate Authority. Shortly after its release, Simon J.K. Pedersen created the excellent <a href="https://github.com/sjkp/letsencrypt-siteextension">letsencrypt-siteextension</a> Azure Web App extension for easy integration with Azure Web Apps. However, at the time of writing it suffers from several issues:</p>
<ul>
<li>The extension must be installed on the same web app as your site.
<ul>
<li>This means you must install the extension on each and every Web App you own.</li>
<li>Worse, if you happen to publish your Web App with the “Delete Existing files”, it will silently delete the WebJob created by the extension, effectively nullifying it.</li>
</ul>
</li>
<li>There are no e-mail notifications (you could set some basic ones with Zapier but they won’t contain details on the actual renewals that took place).</li>
<li>It relies on an Azure Storage account which has to be <a href="https://github.com/sjkp/letsencrypt-siteextension/issues/148">configured in a certain way</a>, which is an unneeded possible point of failure.</li>
<li>The extension can only be run in the context of a web app. You might want to run it as a command-line tool (e.g. from your CI system).</li>
</ul>
<h2 id="solution">Solution</h2>
<p><a href="https://github.com/ohadschn/letsencrypt-webapp-renewer"><code class="highlighter-rouge">letsencrypt-webapp-renewer</code></a> is a WebJob-ready command-line executable I created that builds upon <a href="https://www.nuget.org/packages/letsencrypt.azure.core/">letsencrypt.azure.core</a> (the core component behind letsencrypt-siteextension) to provide the following features:</p>
<ul>
<li>Install on any Web App (doesn’t have to be the same web app for which you want to manage SSL certs).
<ul>
<li>Multiple Web App management is supported.</li>
<li>Publishing with “Delete Existing files” has no effect when the WebJob is deployed to a different (preferably dedicated) Web App.</li>
</ul>
</li>
<li>E-mail notifications are built in (via SendGrid).</li>
<li>No external dependencies other than Let’s Encrypt.</li>
<li>Can be executed as a plain command-line tool from any environment.</li>
</ul>
<p>Head on to <a href="https://github.com/ohadschn/letsencrypt-webapp-renewer">https://github.com/ohadschn/letsencrypt-webapp-renewer</a> to get started!</p>Ohad SchneiderHTTPS is the pervasive standard for all websites, regardless of size or field. The Mozilla foundation has gone so far as to announce their intent to completely phase out HTTP. Unfortunately, the procurement, maintenance, and renewal of SSL/TLS certificates has been an expensive and manual process for many.Synchronizing native Android G Suite apps (GMail, Calendar, etc.) with Outlook.com accounts2016-12-25T18:03:56+00:002016-12-25T18:03:56+00:00https://www.ohadsoft.com/2016/12/synchronizing-native-android-g-suite-apps-gmail-calendar-etc-with-outlook-com-accounts<p><strong>TLDR – scroll to the bottom for a checklist of what you need to do</strong></p>
<p>The task at hand seemed simple enough. I had a GMail account (ohad188@gmail.com) and an Outlook.com account (also ohad188@gmail.com) and I wanted to sync both to my Android device using the <em>native</em> apps, primarily GMail and Calendar (I usually prefer native apps to third party ones such as the <a href="https://play.google.com/store/apps/details?id=com.microsoft.office.outlook&hl=en" target="_blank">Outlook app</a>).But what should have been a trivial exercise ended up as a complicated, non-user friendly ordeal. I’ll share my story here for the benefit of others (and future self).</p>
<p>Adding the GMail account was, naturally, easy and effortless. This is virtually the first thing everyone does when they get their phone, so no surprise there. However, when I tried adding my Outlook.com account I got <em>“Duplicate account – you’re already using this username for the account ohad188@gmail.com”</em>. It’s pretty clear what’s going on here. The GMail app is keying accounts by username only, and since my Outlook.com account happens to have the same username as my Google account, I’m locked out. I was very surprised that such a mature app would have such a trivial limitation, but what can you do. I also couldn’t find much about it on the web, so I decided I’d just work around the limitation (after reporting it to Google). The workaround was simple – I created an ohad188@<strong>outlook.com</strong> <a href="https://support.microsoft.com/en-us/help/12407/microsoft-account-manage-aliases" target="_blank">alias</a> for my account and made it the primary one. This resolved the duplicate account issue, but the story was not over.</p>
<p>While my e-mail seemed to sync fine, I couldn’t see any of my events in the calendar. I looked in every possible setting with no luck. Finally I found an <a href="https://support.office.com/en-us/article/Can-t-sync-calendar-and-contacts-with-my-phone-or-tablet-8479d764-b9f5-4fff-ba88-edd7c265df9f#ID0EACAAA=Android_Gmail_app" target="_blank">MS Office support article</a> that provided a clue for the issue. It turns out that when setting up Outlook.com accounts, you actually have to select <em>Exchange</em> in the account type selection dialog – not Outlook! And as if that’s not bad enough, if you happen to have that account already configured somewhere else (as an Outlook account, IMAP account, or Outlook app account), you won’t be able to add it properly (you will get redirected to the Microsoft account sign-in page which is <strong>not</strong> what you want). So I had to delete all existing references to my Outlook.com account (and removed the Outlook app for good measure) before I could get to the actual Exchange account addition dialog. You will know you’re in the right place when you are asked to provide your password in a native Android dialog (as opposed to a redirected Microsoft sign-in page).</p>
<p>But my troubles did not stop there. After multiple attempts, Android kept insisting that my Outlook.com password was incorrect. Of course, I had just used it successfully when I set up the exact same account as an “Outlook” account a few minutes earlier, and presumable my short-term memory was not damaged in the process. Fortunately, I quickly realized (with no help fom Android or Outlook.com, mind you) that the problem was the 2-factor authentication configured for my account. So I issued an <a href="https://support.microsoft.com/en-us/help/12409/microsoft-account-app-passwords-two-step-verification" target="_blank">app password</a>, provided it in the Exchange account addition dialog, and finally my account was added – mail, calendar, contacts.</p>
<p><strong>So to sum up:</strong></p>
<ol>
<li>Make sure your Outlook.com primary alias/username is different from your GMail address.</li>
<li>Remove the Outlook app and all references to your Outlook.com account in Android’s account settings.</li>
<li>Add your Outlook.com as an <strong>Exchange</strong> account and use an <strong>app password</strong> if 2-factor authenticatino is enabled on your account.</li>
</ol>ohadscTLDR – scroll to the bottom for a checklist of what you need to doTab selects text instead of indenting in Visual Studio2016-12-04T15:43:10+00:002016-12-04T15:43:10+00:00https://www.ohadsoft.com/2016/12/tab-selects-text-instead-of-indenting-in-visual-studio<p>I ran into an annoying phenomena today when I tried indenting some multi-line expression I had in my code. I pressed TAB but instead of indenting, it selected some text. This was driving me mad so I started enabling and disabling extensions (binary search to the rescue) until I isolated the culprit – Resharper.</p>
<p>It turns out that <a href="https://blog.jetbrains.com/dotnet/2016/08/18/resharper-ultimate-2016-2-is-here/" target="_blank">Resharper Ultimate 2016.2 introduced a new <em>Structural navigation</em> feature</a> (enabled by default) that makes tab move text selections instead of indenting when pressed mid-line. I also found out that <a href="https://resharper-support.jetbrains.com/hc/en-us/community/posts/207739969-Tab-key-behaviour-overridden" target="_blank">I wasn’t the first on who complained about it</a>.</p>
<p>Anyway, to disable it simply turn off Structural Navigation in Resharper’s Options (<em>Editor</em> -> <em>Editor Behavior</em>).</p>ohadscI ran into an annoying phenomena today when I tried indenting some multi-line expression I had in my code. I pressed TAB but instead of indenting, it selected some text. This was driving me mad so I started enabling and disabling extensions (binary search to the rescue) until I isolated the culprit – Resharper.Adding reddit RSS feeds to Outlook2016-10-29T11:10:19+00:002016-10-29T11:10:19+00:00https://www.ohadsoft.com/2016/10/adding-reddit-rss-feeds-to-outlook<p><a href="https://www.reddit.com/wiki/rss" target="_blank">Creating reddit RSS feeds</a> is pretty straightforward. For example, subscribing to <code class="highlighter-rouge">https://www.reddit.com/r/news/.rss</code> will get you all threads in the <em>news</em> subreddit. However, attempting to add such a URL to Outlook’s RSS reader will result in the following error:</p>
<blockquote>
<p>Outlook cannot process the RSS content from <code class="highlighter-rouge">https://www.reddit.com/r/news/.rss</code>. The link may not point to a valid RSS source.</p>
</blockquote>
<p>Fortunately, the fix is simple – just get rid of the right-most slash (the one before <code class="highlighter-rouge">.rss</code>): <code class="highlighter-rouge">https://www.reddit.com/r/news.rss</code>.</p>
<p>Outlook will now accept the URL and your subscription will work as expected.</p>ohadscCreating reddit RSS feeds is pretty straightforward. For example, subscribing to https://www.reddit.com/r/news/.rss will get you all threads in the news subreddit. However, attempting to add such a URL to Outlook’s RSS reader will result in the following error:ET4W – generate C# ETW classes from JSON event specifications using T42016-10-21T15:33:14+00:002016-10-21T15:33:14+00:00https://www.ohadsoft.com/2016/10/et4w-generate-c-etw-classes-from-json-event-specifications-using-t4<p><a href="https://github.com/ohadschn/ET4W" target="_blank"><img src="https://raw.githubusercontent.com/ohadschn/ET4W/master/docs/Transformation.png" width="777" height="406" alt="ET4W" class="aligncenter size-medium" /></a></p>
<p><a href="https://msdn.microsoft.com/en-us/library/dn774985(v=pandp.20).aspx" target="_blank">Event Tracing for Windows (ETW)</a> is the best tracing solution for the Windows platform, period. It is unmatched in performance and reliability. More and more tools are built to analyze its events to amazing depth. Microsoft explicitly recommends it for almost any logging purpose.</p>
<p>Unfortunately, writing proper ETW classes is a bit <a href="https://msdn.microsoft.com/en-us/library/dn774985(v=pandp.20).aspx" target="_blank">tricky</a>. It used to be <a href="https://blogs.msdn.microsoft.com/seealso/2011/06/08/use-this-not-this-logging-event-tracing/" target="_blank">much worse</a> (before the advent of automatic manifest generation), but there’s still a lot of code that has to be written manually in a very specific way. Not only is it tedious, it also leaves plenty of room for user error.</p>
<ul>
<li>You have to decorate each event method with <a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventattribute(v=vs.110).aspx" target="_blank"><code>EventAttribute</code></a> (theoretically optional, but in practice you’ll do this for every single method).</li>
<li>You then have to call <a href="https://msdn.microsoft.com/en-us/library/hh393412(v=vs.110).aspx" target="_blank"><code>WriteEvent</code></a> in a very specific manner – the event ID and parameters must match exactly and in order.</li>
<li>Tasks and keywords should be specified using nested classes with a very specific structure.</li>
<li>You are encouraged to expose the class in a very specific singleton pattern.</li>
<li>You must be aware of the exact types supported by ETW (not documented anywhere, so you have to reflect it off <code class="highlighter-rouge">ManifestBuilder.GetTypeName</code>).</li>
<li>If you want to log types that aren’t supported, it’s your responsibility to invoke the appropriate conversions (typically via manually created wrapper classes).</li>
<li>If you want some common parameters to be present in every event, you have to add them manually to each method, and provide them manually in each call.</li>
</ul>
<p>Enter <a href="https://github.com/ohadschn/ET4W" target="_blank">ET4W</a>.</p>
<p>This <a href="https://msdn.microsoft.com/en-us/library/bb126445.aspx" target="_blank">T4 Text Templete</a> based NuGet package will solve all these issues for you. All you have to do is write a JSON file specifying the events you want, as well as the custom types you want to support and the common parameters you want present. There’s even a JSON schema for in-editor validation and auto-completion (supported in Visual Studio and other editors).</p>
<p>ET4W will take care of the rest, creating full-fledged <a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource(v=vs.110).aspx" target="_blank"><code>EventSource</code></a> classes according to all the <a href="https://blogs.msmvps.com/kathleen/2014/01/24/how-are-event-parameters-best-used-to-create-an-intuitive-custom-evnetsourcetrace/" target="_blank">best practices</a> (both <a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing(v=vs.110).aspx" target="_blank">System.Diagnostics.Tracing</a> and <a href="https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.EventSource" target="_blank">Microsoft.Diagnostics.Tracing</a> are supported). In addition, wrapper classes are created to support common parameters and custom types. Finally, a few extra validations are thrown into the mix.</p>
<p>Check it out!</p>
<p><a href="https://github.com/ohadschn/ET4W" target="_blank">https://github.com/ohadschn/ET4W</a> (Apache 2.0)</p>
<p>P.S. one of the nice thing about this approach is that it lends itself well to cross-platform event consistency. By building similar generation scripts for other languages (VB, C++, etc), you could leverage the same event JSON to maintain a “single source of truth” for your events across various platforms (critical for telemetry measurements, for example).</p>ohadscInvoking arbitrary PowerShell commands with retries in PowerShell2016-04-17T15:25:45+00:002016-04-17T15:25:45+00:00https://www.ohadsoft.com/2016/04/invoking-arbitrary-powershell-commands-with-retries-in-powershell<p>Yesterday <a href="https://www.ohadsoft.com/2016/04/invoking-arbitrary-shell-cmd-commands-in-powershell/" target="_blank">I blogged</a> about a function I created to execute arbitrary shell (cmd) commands in PowerShell, with retries in face of failures. But what if the command I want to execute is actually another PowerShell script (or function, or cmdlet)? The rules change a bit:</p>
<ul>
<li>We need to use splatting when making the call (otherwise named parameters won’t work, see the references below).</li>
<li>In addition to splatting the call itself, we also need to format the argument hash table for printing the command (otherwise it would just print something like <code class="highlighter-rouge">Executing: Foo.ps1 System.Collections.Hashtable</code>).</li>
<li><code class="highlighter-rouge">$LASTEXITCODE</code> is no longer (necessarily) relevant, we need to work with PowerShell’s <code class="highlighter-rouge">$?</code> automatic variable.</li>
<li>Parsing the error stream makes less sense, but we need to handle exceptions.</li>
</ul>
<p>Here’s how it looks:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm"><#
.SYNOPSIS
Invokes a command with retries.
.DESCRIPTION
The Call-PSCommandWithRetries function invokes PowerShell scripts, functions, or Cmdlets using the provided parameters, with optional retries in configurable intervals upon failures.
.PARAMETER Command
The command to invoke. Can be any PowerShell script, function, or Cmdlet.
.PARAMETER Arguments
Arguments to pass when invoking the comand (using splatting).
.PARAMETER RetrySleepSeconds
Time in seconds to sleep between retry attempts in case of command failure.
.PARAMETER MaxAttempts
Maximum number of retry attempts in case of command failure.
.PARAMETER PrintCommand
Determines whether or not to print the full command to the host before execution.
.INPUTS
None. You cannot pipe objects to Call-PSCommandWithRetries.
.OUTPUTS
The output of the last command execution.
.EXAMPLE
$output = Call-PSCommandWithRetries "dir" @{"Path"='C:\'}
#></span>
<span class="k">function </span>Call-PSCommandWithRetries
<span class="o">{</span>
<span class="o">[</span><span class="na">CmdletBinding</span><span class="o">()]</span>
<span class="k">Param</span><span class="o">(</span>
<span class="o">[</span>Parameter<span class="o">(</span><span class="nv">Mandatory</span><span class="o">=</span><span class="nv">$True</span><span class="o">)]</span>
<span class="o">[</span><span class="kt">string</span><span class="o">]</span><span class="nv">$Command</span>,
<span class="o">[</span>hashtable]<span class="nv">$Arguments</span>,
<span class="o">[</span><span class="kt">int</span><span class="o">]</span><span class="nv">$RetrySleepSeconds</span> <span class="o">=</span> 10,
<span class="o">[</span><span class="kt">int</span><span class="o">]</span><span class="nv">$MaxAttempts</span> <span class="o">=</span> 10,
<span class="o">[</span><span class="kt">bool</span><span class="o">]</span><span class="nv">$PrintCommand</span> <span class="o">=</span> <span class="nv">$True</span>
<span class="o">)</span>
<span class="k">Process</span>
<span class="o">{</span>
<span class="nv">$attempt</span> <span class="o">=</span> 0
<span class="k">while</span> <span class="o">(</span><span class="nv">$true</span><span class="o">)</span>
<span class="o">{</span>
<span class="nv">$formattedArgs</span> <span class="o">=</span> <span class="nv">$Arguments</span>.Keys.ForEach<span class="o">({</span><span class="s2">"-</span><span class="k">$(</span><span class="nv">$_</span><span class="k">)</span><span class="s2">:</span><span class="k">$(</span><span class="nv">$Arguments</span>.<span class="nv">$_</span><span class="k">)</span><span class="s2">"</span><span class="o">})</span> -join <span class="s1">' '</span>
<span class="nb">Write-Host</span> <span class="k">$(if</span> <span class="o">(</span><span class="nv">$PrintCommand</span><span class="k">)</span> <span class="o">{</span><span class="s2">"Executing: </span><span class="nv">$Command</span><span class="s2"> </span><span class="nv">$formattedArgs</span><span class="s2">"</span><span class="o">}</span> <span class="k">else</span> <span class="o">{</span><span class="s2">"Executing PS command..."</span><span class="o">})</span>
<span class="nv">$exceptionThrown</span> <span class="o">=</span> <span class="nv">$false</span>
<span class="k">try</span>
<span class="o">{</span>
& <span class="nv">$Command</span> @Arguments 2>&1 | <span class="nb">tee</span> -Variable output | <span class="nb">Write-Host</span>
<span class="o">}</span>
<span class="k">catch</span>
<span class="o">{</span>
<span class="nb">Write-Host</span> <span class="s2">"PS command threw exception: </span><span class="k">$(</span><span class="nv">$_</span>.Exception<span class="k">)</span><span class="s2">"</span> -ForegroundColor Yellow
<span class="nv">$exceptionThrown</span> <span class="o">=</span> <span class="nv">$true</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nv">$?</span> -and <span class="o">!(</span><span class="nv">$exceptionThrown</span><span class="o">))</span>
<span class="o">{</span>
<span class="nb">Write-Host</span> <span class="s2">"PS Command executed successfully"</span>
<span class="k">return</span> <span class="nv">$output</span>
<span class="o">}</span>
<span class="nb">Write-Host</span> <span class="s2">"PS Command failed"</span> -ForegroundColor Yellow
<span class="k">if</span> <span class="o">(</span><span class="nv">$attempt</span> -eq <span class="nv">$MaxAttempts</span><span class="o">)</span>
<span class="o">{</span>
<span class="nv">$ex</span> <span class="o">=</span> <span class="nb">new-object </span>System.Management.Automation.CmdletInvocationException <span class="s2">"All retry attempts exhausted"</span>
<span class="nv">$category</span> <span class="o">=</span> <span class="o">[</span>System.Management.Automation.ErrorCategory]::LimitsExceeded
<span class="nv">$errRecord</span> <span class="o">=</span> <span class="nb">new-object </span>System.Management.Automation.ErrorRecord <span class="nv">$ex</span>, <span class="s2">"CommandFailed"</span>, <span class="nv">$category</span>, <span class="nv">$Path</span>
<span class="nv">$psCmdlet</span>.WriteError<span class="o">(</span><span class="nv">$errRecord</span><span class="o">)</span>
<span class="k">return</span> <span class="nv">$output</span>
<span class="o">}</span>
<span class="nv">$attempt</span>++;
<span class="nb">Write-Host</span> <span class="s2">"Retrying test execution [#</span><span class="nv">$attempt</span><span class="s2">/</span><span class="nv">$MaxAttempts</span><span class="s2">] in </span><span class="nv">$RetrySleepSeconds</span><span class="s2"> seconds..."</span>
<span class="nb">Start-Sleep</span> -s <span class="nv">$RetrySleepSeconds</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>References:</p>
<ul>
<li><a href="https://stackoverflow.com/questions/10666101/lastexitcode-0-but-false-in-powershell-redirecting-stderr-to-stdout-gives-n" target="_blank">https://stackoverflow.com/questions/10666101/lastexitcode-0-but-false-in-powershell-redirecting-stderr-to-stdout-gives-n</a></li>
<li><a href="https://stackoverflow.com/questions/36164888/using-the-powershell-call-operator-to-call-powershell-scripts-with-switch-pa" target="_blank">https://stackoverflow.com/questions/36164888/using-the-powershell-call-operator-to-call-powershell-scripts-with-switch-pa</a></li>
</ul>ohadscYesterday I blogged about a function I created to execute arbitrary shell (cmd) commands in PowerShell, with retries in face of failures. But what if the command I want to execute is actually another PowerShell script (or function, or cmdlet)? The rules change a bit:Invoking arbitrary shell (cmd) commands with retries in PowerShell2016-04-16T18:25:16+00:002016-04-16T18:25:16+00:00https://www.ohadsoft.com/2016/04/invoking-arbitrary-shell-cmd-commands-in-powershell<p><em><strong>EDIT</strong>– I initially tried to support execution of PowerShell scripts, functions, and cmdlets in the code below. However, it turns out that they behave too differently so I wrote a separate post on it: <a href="https://www.ohadsoft.com/2016/04/invoking-arbitrary-powershell-commands-with-retries-in-powershell/">https://www.ohadsoft.com/2016/04/invoking-arbitrary-powershell-commands-with-retries-in-powershell/</a>.</em></p>
<p>It may sound ironic, but executing shell (cmd) commands from PowerShell is not always trivial. There are various possibilities including <code class="highlighter-rouge">Start-Process</code>, <code class="highlighter-rouge">[Diagnostics.Process]::Start</code>, the call operator (&), and others. Each has its own quirks regarding exit codes, output redirection, argument escaping, and more. For a taste of the issues you might run into, see this <a href="http://edgylogic.com/blog/powershell-and-external-commands-done-right/" target="_blank">blog post by edgylogic</a>.</p>
<p>Having played around with most (all?) of the options, I finally settled down on what I percieve to be the most robust, versatile, and simple way of doing it. The (advanced) function below will allow the following:</p>
<ul>
<li>Print the command before execution.</li>
<li>Inspect the exit code and error stream to determine failure.</li>
<li>Retry the command in configurable intervals upon failure.</li>
<li>Pass multiple parameters.</li>
<li>Emit the command’s output to the pipe for further processing.</li>
<li>Set the execution status ($?) to <code class="highlighter-rouge">$False</code> in case of failre on all retries.</li>
</ul>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm"><#
.SYNOPSIS
Calls a shell (cmd) command with retries
.DESCRIPTION
The Call-CommandWithRetries function calls shell (cmd) commands using the provided parameters, with optional retries in configurable intervals upon failures.
.PARAMETER Command
The command to call.
.PARAMETER Arguments
Arguments to pass when invoking the comand.
.PARAMETER TrustExitCode
Trust the command's exit code for the purpose of determining whether it was successful or not.
If this parameter is $False, a non-empty stderr will also be considered a failure.
.PARAMETER RetrySleepSeconds
Time in seconds to sleep between retry attempts in case of command failure.
.PARAMETER MaxAttempts
Maximum number of retry attempts in case of command failure.
.PARAMETER PrintCommand
Determines whether or not to print the full command to the host before execution.
.INPUTS
None. You cannot pipe objects to Call-CommandWithRetries.
.OUTPUTS
The output of the last command execution.
.EXAMPLE
Use cURL for Windows to download the latest NuGet command-line client
C:\PS> Call-CommandWithRetries "curl.exe" @("--fail", "-O", "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe")
#></span>
<span class="k">function </span>Call-CommandWithRetries
<span class="o">{</span>
<span class="o">[</span><span class="na">CmdletBinding</span><span class="o">()]</span>
<span class="k">param</span><span class="o">(</span>
<span class="o">[</span>Parameter<span class="o">(</span><span class="nv">Mandatory</span><span class="o">=</span><span class="nv">$True</span><span class="o">)]</span>
<span class="o">[</span><span class="kt">string</span><span class="o">]</span><span class="nv">$Command</span>,
<span class="o">[</span>Array]<span class="nv">$Arguments</span>,
<span class="o">[</span><span class="kt">bool</span><span class="o">]</span><span class="nv">$TrustExitCode</span> <span class="o">=</span> <span class="nv">$True</span>,
<span class="o">[</span><span class="kt">int</span><span class="o">]</span><span class="nv">$RetrySleepSeconds</span> <span class="o">=</span> 10,
<span class="o">[</span><span class="kt">int</span><span class="o">]</span><span class="nv">$MaxAttempts</span> <span class="o">=</span> 10,
<span class="o">[</span><span class="kt">bool</span><span class="o">]</span><span class="nv">$PrintCommand</span> <span class="o">=</span> <span class="nv">$True</span>
<span class="o">)</span>
<span class="k">Process</span>
<span class="o">{</span>
<span class="nv">$attempt</span> <span class="o">=</span> 0
<span class="k">while</span> <span class="o">(</span><span class="nv">$true</span><span class="o">)</span>
<span class="o">{</span>
<span class="nb">Write-Host</span> <span class="k">$(if</span> <span class="o">(</span><span class="nv">$PrintCommand</span><span class="k">)</span> <span class="o">{</span><span class="s2">"Executing: </span><span class="nv">$Command</span><span class="s2"> </span><span class="nv">$Arguments</span><span class="s2">"</span><span class="o">}</span> <span class="k">else</span> <span class="o">{</span><span class="s2">"Executing command..."</span><span class="o">})</span>
& <span class="nv">$Command</span> <span class="nv">$Arguments</span> 2>&1 | <span class="nb">tee</span> -Variable output | <span class="nb">Write-Host</span>
<span class="nv">$stderr</span> <span class="o">=</span> <span class="nv">$output</span> | <span class="nb">where</span> <span class="o">{</span> <span class="nv">$_</span> -is <span class="o">[</span>System.Management.Automation.ErrorRecord] <span class="o">}</span>
<span class="k">if</span> <span class="o">(</span> <span class="o">(</span><span class="nv">$LASTEXITCODE</span> -eq 0<span class="o">)</span> -and <span class="o">(</span><span class="nv">$TrustExitCode</span> -or <span class="o">!(</span><span class="nv">$stderr</span><span class="o">))</span> <span class="o">)</span>
<span class="o">{</span>
<span class="nb">Write-Host</span> <span class="s2">"Command executed successfully"</span>
<span class="k">return</span> <span class="nv">$output</span>
<span class="o">}</span>
<span class="nb">Write-Host</span> <span class="s2">"Command failed with exit code (</span><span class="nv">$LASTEXITCODE</span><span class="s2">) and stderr: </span><span class="nv">$stderr</span><span class="s2">"</span> -ForegroundColor Yellow
<span class="k">if</span> <span class="o">(</span><span class="nv">$attempt</span> -eq <span class="nv">$MaxAttempts</span><span class="o">)</span>
<span class="o">{</span>
<span class="nv">$ex</span> <span class="o">=</span> <span class="nb">new-object </span>System.Management.Automation.CmdletInvocationException <span class="s2">"All retry attempts exhausted"</span>
<span class="nv">$category</span> <span class="o">=</span> <span class="o">[</span>System.Management.Automation.ErrorCategory]::LimitsExceeded
<span class="nv">$errRecord</span> <span class="o">=</span> <span class="nb">new-object </span>System.Management.Automation.ErrorRecord <span class="nv">$ex</span>, <span class="s2">"CommandFailed"</span>, <span class="nv">$category</span>, <span class="nv">$Command</span>
<span class="nv">$psCmdlet</span>.WriteError<span class="o">(</span><span class="nv">$errRecord</span><span class="o">)</span>
<span class="k">return</span> <span class="nv">$output</span>
<span class="o">}</span>
<span class="nv">$attempt</span>++;
<span class="nb">Write-Host</span> <span class="s2">"Retrying test execution [#</span><span class="nv">$attempt</span><span class="s2">/</span><span class="nv">$MaxAttempts</span><span class="s2">] in </span><span class="nv">$RetrySleepSeconds</span><span class="s2"> seconds..."</span>
<span class="nb">Start-Sleep</span> -s <span class="nv">$RetrySleepSeconds</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>References:</p>
<ul>
<li><a href="https://stackoverflow.com/questions/10666035/difference-between-and-lastexitcode-in-powershell" target="_blank">https://stackoverflow.com/questions/10666035/difference-between-and-lastexitcode-in-powershell</a></li>
<li><a href="https://stackoverflow.com/questions/24222088/powershell-capture-program-stdout-and-stderr-to-seperate-variables" target="_blank">https://stackoverflow.com/questions/24222088/powershell-capture-program-stdout-and-stderr-to-seperate-variables</a></li>
</ul>ohadscEDIT– I initially tried to support execution of PowerShell scripts, functions, and cmdlets in the code below. However, it turns out that they behave too differently so I wrote a separate post on it: https://www.ohadsoft.com/2016/04/invoking-arbitrary-powershell-commands-with-retries-in-powershell/.Creating beautiful WordPress header images for free – legally2016-04-04T21:03:47+00:002016-04-04T21:03:47+00:00https://www.ohadsoft.com/2016/04/creating-beautiful-wordpress-header-images-for-free-legally<p>Personally, I like using the official WordPress.org themes as they always seem to look professional and clean. Moreover, they are very unlikely to contain bugs or compatibility issues like I’ve seen in the past with various other themes I tried. Consequently, I recently updated my blog’s theme to <a href="https://wordpress.org/themes/twentysixteen/" target="_blank">Twenty Sixteen</a>. To my surprise though, no header image was included (it is included in other WordPress themes) and the top section seemed a little barren.</p>
<p>A quick web search for <a href="https://www.google.co.il/search?q=free+wordpress+header+images" target="_blank">free WordPress header images</a> turned up a few sites that did have some nice photos, but required you to link back to them which is a bit cumbersome. Fortunately, I realized there was a better way.</p>
<p>Enter <a href="http://www.freeimages.com" target="_blank">Freeimages</a>. This neat site contains almost <em>400,000 free images</em> – many of them free even for commercial use (if you have ads on your site, it is considered commercial). You can also usually modify the image (so cropping it to fit your theme should be OK), and for the most part not even attribution is necessary! Of course, you should <strong>always</strong> check the license on each image to be sure.</p>
<p>So in a nutshell, here’s what you need to do:</p>
<ol>
<li><a href="http://www.freeimages.com/search/landscape" target="_blank">Search for some pictures you might like for your header</a></li>
<li>Pick the one you like the most (or several, if you’d like to rotate them)</li>
<li>Download a high-res copy so that WordPress only has to crop it and/or scale it down (rather than scale it up / stretch it)</li>
<li>Make sure you abide by the license</li>
<li>Upload and crop the image (via the WordPress theme customizer)</li>
</ol>
<p>If you don’t find an image you fancy in that particular site, <a href="https://www.google.co.il/search?q=free+stock+photos" target="_blank">there are many others</a> you can browse. See if you can spot the image you’re currently seeing at the top of this page 😉</p>
<p>Happy hunting!</p>ohadscPersonally, I like using the official WordPress.org themes as they always seem to look professional and clean. Moreover, they are very unlikely to contain bugs or compatibility issues like I’ve seen in the past with various other themes I tried. Consequently, I recently updated my blog’s theme to Twenty Sixteen. To my surprise though, no header image was included (it is included in other WordPress themes) and the top section seemed a little barren.Redirecting all URLs to their HTTPS WWW equivalents2016-04-02T17:54:02+00:002016-04-02T17:54:02+00:00https://www.ohadsoft.com/2016/04/redirecting-all-urls-to-their-https-www-equivalents<p>Whether you’re a <a href="http://no-www.org/" target="_blank">no-www</a> or a <a href="http://www.yes-www.org/" target="_blank">yes-www</a> person (you can guess which one I am based on the URL currently present on your address bar 🙂<span>)</span>, one thing is certain – you need to be consistent. This means that if you like <em>www</em>, then <em>yourdomain.com</em> should redirect (301 permanent) to <em>www.yourdomain.com</em>, and if you don’t like it then the redirection needs to go the other way around. Either way, you can’t have people browsing the same page from different domain (with <em>www</em> and without it), it’s just confusing.</p>
<p>In addition, you really should be using <a href="https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/" target="_blank">SSL everywhere, all the time</a>. Certificates <a href="https://letsencrypt.org/" target="_blank">are</a> <a href="https://www.startssl.com/Support?v=1" target="_blank">free</a>, and even if your hosting plan doesn’t support them (as is the case with Azure’s free tier), you can <a href="https://www.troyhunt.com/2015/04/how-to-get-your-ssl-for-free-on-shared.html" target="_blank">have CloudFlare take care of that for you</a> (and enjoy improved performance and security as a bonus). Once you have SSL up and running, you’d want to redirect all HTTP traffic to HTTPS and enable <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank">HSTS</a> for increased security.</p>
<p>To recap – once you’ve decided whether you like <em>www</em> or not, and finished setting up SSL, you want your redirections to look like this (assuming <em>yes-www</em>):</p>
<ul>
<li><em>HTTP://yourdomain.com</em> -> <em>HTTPS://www.yourdomain.com</em></li>
<li><em>HTTP://www.yourdomain.com</em> -> <em>HTTPS://www.yourdomain.com</em></li>
<li><em>HTTPS://yourdomain.com</em> -> <em>HTTPS://www.yourdomain.com</em></li>
</ul>
<p>In addition, you want HSTS headers (<em>Strict_Transport_Security</em>) in place. Fortunately, using the <a href="https://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-20-configuration-reference" target="_blank">IIS Rewrite module</a> (installed by default on Azure deployments), accomplishing all of the above is a breeze:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><system.webServer></span>
<span class="nt"><rewrite></span>
<span class="nt"><rules></span>
<span class="nt"><rule</span> <span class="na">name=</span><span class="s">"Redirect to www"</span> <span class="na">stopProcessing=</span><span class="s">"true"</span><span class="nt">></span>
<span class="nt"><match</span> <span class="na">url=</span><span class="s">"(.*)"</span> <span class="nt">/></span>
<span class="nt"><conditions</span> <span class="na">logicalGrouping=</span><span class="s">"MatchAny"</span><span class="nt">></span>
<span class="nt"><add</span> <span class="na">input=</span><span class="s">"{HTTP_HOST}"</span> <span class="na">pattern=</span><span class="s">"^yourdomain\.com"</span><span class="nt">/></span>
<span class="nt"><add</span> <span class="na">input=</span><span class="s">"{HTTPS}"</span> <span class="na">pattern=</span><span class="s">"off"</span> <span class="na">ignoreCase=</span><span class="s">"true"</span><span class="nt">/></span>
<span class="nt"></conditions></span>
<span class="nt"><action</span> <span class="na">type=</span><span class="s">"Redirect"</span>
<span class="na">url=</span><span class="s">"https://www.yourdomain.com/{R:1}"</span>
<span class="na">redirectType=</span><span class="s">"Permanent"</span><span class="nt">/></span>
<span class="nt"></rule></span>
<span class="nt"></rules></span>
<span class="nt"><outboundRules></span>
<span class="nt"><rule</span> <span class="na">name=</span><span class="s">"HSTS"</span> <span class="na">enabled=</span><span class="s">"true"</span><span class="nt">></span>
<span class="nt"><match</span>
<span class="na">serverVariable=</span><span class="s">"RESPONSE_Strict_Transport_Security"</span>
<span class="na">pattern=</span><span class="s">".*"</span> <span class="nt">/></span>
<span class="nt"><conditions></span>
<span class="nt"><add</span> <span class="na">input=</span><span class="s">"{HTTPS}"</span> <span class="na">pattern=</span><span class="s">"on"</span> <span class="na">ignoreCase=</span><span class="s">"true"</span> <span class="nt">/></span>
<span class="nt"></conditions></span>
<span class="nt"><action</span> <span class="na">type=</span><span class="s">"Rewrite"</span> <span class="na">value=</span><span class="s">"max-age=31536000"</span> <span class="nt">/></span>
<span class="nt"></rule></span>
<span class="nt"></outboundRules></span>
<span class="nt"></rewrite></span>
<span class="nt"></system.webServer></span>
<span class="nt"></configuration></span>
</code></pre></div></div>
<p>Happy rewriting 😉</p>ohadscWhether you’re a no-www or a yes-www person (you can guess which one I am based on the URL currently present on your address bar 🙂), one thing is certain – you need to be consistent. This means that if you like www, then yourdomain.com should redirect (301 permanent) to www.yourdomain.com, and if you don’t like it then the redirection needs to go the other way around. Either way, you can’t have people browsing the same page from different domain (with www and without it), it’s just confusing.Gtk-WARNING **: Locale not supported by C library2016-02-20T17:05:16+00:002016-02-20T17:05:16+00:00https://www.ohadsoft.com/2016/02/gtk-warning-locale-not-supported-by-c-library<p>I was recently writing a mono application for OSX in MonoDevelop, using <a href="https://www.mono-project.com/docs/gui/gtksharp/">Gtk#</a> as the GUI framework. The app seemed to work fine, but it would throw the following error each time it started:</p>
<pre class="brush: plain; title: ; notranslate" title="">Gtk-WARNING **: Locale not supported by C library</pre>
<p>Since I don’t like errors, even if I don’t understand their immediate effect, I did a bit of searching. My search led me to this <a href="https://stackoverflow.com/questions/7165108/in-osx-lion-lang-is-not-set-to-utf8-how-fix">StackOverflow post</a>, whose answers suggested defining the following environment variables in the terminal:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">LC_ALL</span><span class="o">=</span>en_US.UTF-8
<span class="nb">export </span><span class="nv">LANG</span><span class="o">=</span>en_US.UTF-8
</code></pre></div></div>
<p>Indeed, setting those variables in a bash script, prior to the execution of my application, resolved the issue 🙂</p>ohadscI was recently writing a mono application for OSX in MonoDevelop, using Gtk# as the GUI framework. The app seemed to work fine, but it would throw the following error each time it started: