<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Andrew Pearce</title>
    <description>PowerShell. AWS. Automation.</description>
    <link>https://andrewpearce.io//</link>
    <atom:link href="https://andrewpearce.io//feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 13 Apr 2019 05:14:19 +0000</pubDate>
    <lastBuildDate>Sat, 13 Apr 2019 05:14:19 +0000</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>Speaking at the PowerShell and DevOps Global Summit 2019</title>
        <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Disclaimer: The views expressed on here are mine alone and not (necessarily) those of my employer (AWS).&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a few weeks I will be attending the PowerShell and DevOps Global Summit 2019, my fourth PowerShell Summit in as many years since joining Amazon in late 2015. The biggest difference this year is I’m attending as a speaker, presenting on two of my favorite topics, Serverless computing with &lt;a href=&quot;https://github.com/aws/aws-lambda-dotnet/tree/master/PowerShell&quot;&gt;PowerShell in AWS Lambda&lt;/a&gt; and how the &lt;a href=&quot;https://docs.aws.amazon.com/kinesis-agent-windows/latest/userguide/what-is-kinesis-agent-windows.html&quot;&gt;Kinesis Agent for Microsoft Windows&lt;/a&gt; can help make your logs more useful.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The PowerShell language support in AWS Lambda is one of my favorite topics. It’s no secret that I’m a huge fan of Serverless computing and the operational benefits it can provide to engineering teams. In September 2018, when &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2018/09/aws-lambda-supports-powershell-core/&quot;&gt;AWS Lambda launched support for PowerShell Core&lt;/a&gt;, it opened the doors for the PowerShell community to leverage the benefits of Serverless computing without switching to another language, and in my opinion, it is a &lt;strong&gt;big&lt;/strong&gt; deal for the PowerShell community. AWS Lambda lets you run code without provisioning or managing servers. It is highly available by default, has automatic scaling and charges you only when your code is executed.&lt;/p&gt;

&lt;p&gt;The benefits of AWS Lambda have already existed for developers leveraging other runtimes, such as Python, .NET Core or Node.js, however a lot of developers in the PowerShell community are keen to stay with PowerShell where they can, which can make a lot of sense. For example, at one end of the scale are the developers who want to choose the best language for every situation, and at the other end are those who only ever want to learn and develop in a single language. However there’s often an even more important decision that engineering teams need to make, one of operational excellence. At 2am when your on-call gets woken up by the annoying, horrifying sound of their pager, what language do they want to be troubleshooting? The language they’re the most familiar with? Or the language they only just started to learn because “it was the best language for the situation”? Don’t get me wrong, I’m all for engineers learning as many languages as they can as I believe it makes them a stronger engineer, however when you’re building that next production service, language familiarity for the engineering team has to be part of the decision.&lt;/p&gt;

&lt;p&gt;One of the reasons I’m such a big fan of leveraging Serverless, is it’s abstractions. With Serverless, engineering teams can go about choosing the best language for &lt;strong&gt;&lt;em&gt;them, their service, and their operational excellence&lt;/em&gt;&lt;/strong&gt;. Customers likely don’t care what language a service is developed in, and they likely don’t care about the engineering team being paged at 2am page for a service outage; they just want the service back online as soon as possible. For a lot of administrators, engineers and developers attending the PowerShell and DevOps Global Summit, PowerShell is likely going to be one of the best &lt;strong&gt;operational&lt;/strong&gt; decisions.&lt;/p&gt;

&lt;p&gt;To circle back, having the PowerShell Core language support available in AWS Lambda is a game changer. It opens the door to all the capabilities of AWS and Serverless for PowerShell developers.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Switching focus a little to the Kinesis Agent for Microsoft Windows. I think this agent is one of the best ways for Windows focused engineering teams to start &lt;strong&gt;&lt;em&gt;using&lt;/em&gt;&lt;/strong&gt; their logs to deliver service improvements. The plugins included in the default installation allow you to stream your logs and performance counters to services such as &lt;a href=&quot;https://aws.amazon.com/kinesis/&quot;&gt;Amazon Kinesis&lt;/a&gt; or &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;Amazon CloudWatch&lt;/a&gt;. Once your data is streamed to these services, you can do almost anything you can think of… stream them into Amazon Elasticsearch, dump them to S3 for both archival and SQL-like queries with Amazon Athena or process them in near-realtime with PowerShell in AWS Lambda. Think about that last one for a second… you can process your logs in a centralized location, with a service that scales automatically, with code you develop, to do almost anything you want. That is a &lt;strong&gt;very&lt;/strong&gt; powerful capability.&lt;/p&gt;

&lt;p&gt;Centralizing your logs is a key requirement as you move towards disposable systems. It’s great that when you’re woken up in the middle of the night you can forget about everything and simply terminate the EC2 Instance, allowing it to rebuild and get your service back to an operational state. The downside is that without centralized logs you’ll also lose the data required to help perform a root cause analysis so you can prevent the issue from re-occurring. The Kinesis Agent for Microsoft Windows can help get you out of that situation, while also allowing you to take action from your logs and leveraging them in ways you may never have thought possible.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;So there you have it, a little insight into why I wanted to present on these two topics, and why I’m excited to share some knowledge about these services and their capabilities. It’s likely to be a very different experience for me at this years PowerShell and DevOps Global Summit, but it’s one that I’m definitely looking forward to. It will be great to catch up with the community members I’ve gotten to know over the years, and to meet those I haven’t. See you all there in a few short weeks!&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Apr 2019 21:00:00 +0000</pubDate>
        <link>https://andrewpearce.io//powershell/2019/04/12/speaking-at-powershell-summit/</link>
        <guid isPermaLink="true">https://andrewpearce.io//powershell/2019/04/12/speaking-at-powershell-summit/</guid>
        
        <category>aws</category>
        
        <category>powershell</category>
        
        <category>speaking</category>
        
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>Cross-platform PowerShell, Unit Testing and Automatic Variables</title>
        <description>&lt;p&gt;Now that PowerShell Core is gaining traction in the community, it’s only a matter of time before cross-platform PowerShell Module development is the standard. Let’s face it, it’s a no-brainer, why would you exclude potential customers!? However we need to remember that PowerShell Core is not equal to or the same as Windows PowerShell. They are different products, with different cmdlets, namespaces and functionality, and I have no doubt you’ll end up with platform specific logic paths in your code (at least in the near term).&lt;/p&gt;

&lt;p&gt;In addition to this, PowerShell developers are increasingly using CI/CD tooling to automate their unit tests on multiple Operating Systems; whether it’s with &lt;a href=&quot;https://www.appveyor.com/&quot;&gt;AppVeyor&lt;/a&gt; and &lt;a href=&quot;https://travis-ci.org/&quot;&gt;TravisCI&lt;/a&gt;, or perhaps with &lt;a href=&quot;https://aws.amazon.com/codebuild/&quot;&gt;AWS CodeBuild&lt;/a&gt;, which now supports Linux and &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2018/05/aws-codebuild-adds-support-for-windows-builds/&quot;&gt;Windows&lt;/a&gt; build containers.&lt;/p&gt;

&lt;p&gt;This blog will provide insight and guidance into a common issue PowerShell developers are likely to face when they start their cross-platform development, specifically in regards to PowerShell automatic variables and &lt;a href=&quot;https://github.com/pester/Pester&quot;&gt;Pester&lt;/a&gt; unit tests.&lt;/p&gt;

&lt;h2 id=&quot;a-little-background&quot;&gt;A little background&lt;/h2&gt;

&lt;p&gt;With the release of Nano Server and Windows IOT, ‘Windows PowerShell Core’ was released, running on .NET Core as those platforms didn’t have the full .NET stack available. To help developers identify which platform their code was executing on, the Constant variable ‘$PSEdition’ was introduced. It resolves to ‘Desktop’ on Windows Server and Windows client platforms, and ‘Core’ on Nano Server and Windows IOT.&lt;/p&gt;

&lt;p&gt;When PowerShell Core was released cross-platform, a similar issue was introduced; PowerShell developers still needed a way to identify the underlying platform. The automatic variables ‘$IsCoreCLR’, ‘$IsLinux’, ‘$IsMacOS’ and ‘$IsWindows’ were introduced to resolve the issue. ‘$IsCoreCLR’ is true on all PowerShell Core platforms, and it’s pretty obvious when the other variables would evaluate to true or false.&lt;/p&gt;

&lt;p&gt;PowerShell developers have five automatic variables to identify the underlying platform for cross-platform PowerShell development. However, if you’re in a mature environment or even just starting out writing Pester unit tests, rest assured that when you use these automatic variables directly in your code you will very likely run into unit testing troubles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The PowerShell &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables&quot;&gt;automatic variables documentation&lt;/a&gt; does not include details about these recently introduced variables yet.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;automatic-variables-and-unit-testing-dont-mix&quot;&gt;Automatic variables and unit testing don’t mix&lt;/h2&gt;

&lt;p&gt;I’m going to walk through what most PowerShell developers do when they start developing cross-platform; the goal is to write a function that returns ‘Windows’ or ‘Not Windows’. I am going to ignore both Nano Server and Windows IOT for this example as they aren’t really end-user, customer platforms in most cases.&lt;/p&gt;

&lt;p&gt;I’ll start with a simple function and unit tests to demonstrate the logic works. I also note that ‘$IsWindows’ is now an option so I’ll use that.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# The sample code&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;Invoke-Foo
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

Describe &lt;span class=&quot;s1&quot;&gt;'Invoke-Foo'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    It &lt;span class=&quot;s1&quot;&gt;'Runs the Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;s1&quot;&gt;'Runs the non-Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$false&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Code written, lets run our tests:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 29ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the non-Windows logic path 18ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Perfect! The tests passed; our code works. Now lets go try out our new function:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Invocation of 'Invoke-Foo' on Windows PowerShell&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Not Windows

&lt;span class=&quot;c1&quot;&gt;# Invocation of 'Invoke-Foo' on PowerShell Core on Windows&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Windows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well, that didn’t work as planned did it. Clearly something needs to change. Now we remember that ‘$IsWindows’ is only valid for PowerShell Core, so we’ll add ‘$PSEdition’ to our evaluation to fix the function on Windows PowerShell:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# PSEdition check added&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;Invoke-Foo
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; -or &lt;span class=&quot;nv&quot;&gt;$PSEdition&lt;/span&gt; -eq &lt;span class=&quot;s1&quot;&gt;'Desktop'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Setting PSEdition to 'Core' so we can test the non-Windows path&lt;/span&gt;
Describe &lt;span class=&quot;s1&quot;&gt;'Invoke-Foo'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    It &lt;span class=&quot;s1&quot;&gt;'Runs the Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;s1&quot;&gt;'Runs the non-Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$false&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$PSEdition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Core'&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s run our tests and invoke the function to validate:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Pester Test&lt;/span&gt;
Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 32ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;-] Runs the non-Windows logic path 14ms
    SessionStateUnauthorizedAccessException: Cannot overwrite variable PSEdition because it is read-only or constant.

&lt;span class=&quot;c1&quot;&gt;# Invocation of 'Invoke-Foo' on Windows PowerShell&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Windows

&lt;span class=&quot;c1&quot;&gt;# Invocation of 'Invoke-Foo' on PowerShell Core on Windows&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Windows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We now have a working function, but our unit tests fail so we can’t prove it. Why is the test failing? Because we can’t modify the automatic variable ‘$PSEdition’, which is a Constant. A Constant variable as outlined in the ‘Option’ parameter of &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/set-variable?view=powershell-6&quot;&gt;Set-Variable&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The acceptable values for this parameter are:

None. Sets no options. (&quot;None&quot; is the default.)
ReadOnly. Can be deleted. Cannot be not changed, except by using the Force parameter.
Constant. Cannot be deleted or changed. Constant is valid only when you are creating a variable. You cannot change the options of an existing variable to Constant.
Private. The variable is available only in the current scope.
AllScope. The variable is copied to any new scopes that are created.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That Constant ‘$PSEdition’ variable cannot be modified, which makes is essentially useless in our unit test.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Some of you are probably seeing the underlying problem and have already figured out the fix, however I’ll continue walking through the process as I believe this is a very common workflow when PowerShell developers start developing for cross-platform. Additionally, the next step for a lot of developers is to drop the unit tests because “the code works!”. This is very clearly the &lt;em&gt;wrong&lt;/em&gt; answer.&lt;/p&gt;

&lt;h2 id=&quot;so-how-is-this-resolved&quot;&gt;So how is this resolved?&lt;/h2&gt;

&lt;p&gt;The answer is actually quite simple! Firstly, &lt;em&gt;do not&lt;/em&gt; drop the unit tests. These are critical to operational maturity, your personal development and is simply good development practice. To resolve this,  we are going to use another function instead of directly calling the automatic variables. The new function will perform the exact same logic we used initially, except we can mock the function with whatever we want, which means we can control the logic and validate it.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# A function to evaluate the platform&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;IsWindows
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$IsWindows&lt;/span&gt; -or &lt;span class=&quot;nv&quot;&gt;$PSEdition&lt;/span&gt; -eq &lt;span class=&quot;s1&quot;&gt;'Desktop'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$false&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Our user facing public function&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;Invoke-Foo
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;IsWindows&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Unit tests now mock the private function&lt;/span&gt;
Describe &lt;span class=&quot;s1&quot;&gt;'Invoke-Foo'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    It &lt;span class=&quot;s1&quot;&gt;'Runs the Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        Mock IsWindows &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;s1&quot;&gt;'Runs the non-Windows logic path'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        Mock IsWindows &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Foo
        &lt;span class=&quot;nv&quot;&gt;$assertion&lt;/span&gt; | Should -BeExactly &lt;span class=&quot;s1&quot;&gt;'Not Windows'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This change makes the code just as easy to read, and if used in a PowerShell Module, the ‘IsWindows’ function would be made private. To demonstrate that this change has not broken the function while allowing us to perform our unit testing validation:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#############&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Pester&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#############&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Unit tests on Windows PowerShell&lt;/span&gt;
Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 63ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the non-Windows logic path 48ms

&lt;span class=&quot;c1&quot;&gt;# Unit tests on PowerShell Core on Windows&lt;/span&gt;
Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 136ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the non-Windows logic path 27ms

&lt;span class=&quot;c1&quot;&gt;# Unit tests in PowerShell Core in Bash WSL&lt;/span&gt;
Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 606ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the non-Windows logic path 123ms

&lt;span class=&quot;c1&quot;&gt;# Unit tests in PowerShell Core on MacOS&lt;/span&gt;
Describing Invoke-Foo
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the Windows logic path 653ms
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+] Runs the non-Windows logic path 123ms

&lt;span class=&quot;c1&quot;&gt;#############&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Invocation&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#############&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Windows PowerShell&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Windows

&lt;span class=&quot;c1&quot;&gt;# PowerShell Core on Windows&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;Invoke-Foo
Windows

&lt;span class=&quot;c1&quot;&gt;# PowerShell Core in Bash WSL&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt; /&amp;gt; Invoke-Foo
Not Windows

&lt;span class=&quot;c1&quot;&gt;# PowerShell Core on MacOS&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;PS&lt;/span&gt; /&amp;gt; Invoke-Foo
Not Windows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap up&lt;/h2&gt;

&lt;p&gt;Automatic variables are extremely useful and provide the required detail to identify the platform your PowerShell code is being executed on. However, remember that using them directly can cause you headaches. Simply swap them out for private helper functions and off you go.&lt;/p&gt;

&lt;p&gt;Happy PowerShell Developing!&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Jul 2018 21:00:00 +0000</pubDate>
        <link>https://andrewpearce.io//powershell/2018/07/10/cross-platform-pester-gotcha/</link>
        <guid isPermaLink="true">https://andrewpearce.io//powershell/2018/07/10/cross-platform-pester-gotcha/</guid>
        
        <category>powershell</category>
        
        
        <category>powershell</category>
        
      </item>
    
      <item>
        <title>Announcing 'Convert', a PowerShell Module for common object conversions.</title>
        <description>&lt;p&gt;I recently released a module to the &lt;a href=&quot;https://www.powershellgallery.com/packages/Convert&quot;&gt;PowerShell Gallery&lt;/a&gt;, called simply, &lt;strong&gt;Convert&lt;/strong&gt;. Convert provides PowerShell Functions for common object conversions. The initial release enables conversions to and from base64 encoded strings, clixml, and MemoryStream objects with optional gzip compression. It wraps the appropriate .NET methods with easy to use PowerShell Functions. The module is cross-platform ready and comes with a minimum requirement of PowerShell 5, which I believe is fair game for 2018.&lt;/p&gt;

&lt;p&gt;Installation is the standard process from the PowerShell Gallery:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Module -Name &lt;span class=&quot;s1&quot;&gt;'Convert'&lt;/span&gt; -Scope &lt;span class=&quot;s1&quot;&gt;'CurrentUser'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I hear some of you asking, “what’s the benefit of a module that simply wraps standard .NET calls?” My answer, &lt;em&gt;simplicity&lt;/em&gt;. I often perform object conversions between these object types, and I find myself forgetting the underlying .NET Namespace, or the conversion process. Instead, I want to use PowerShell to make this easier, and with Convert, hopefully to help other people while I’m at it.&lt;/p&gt;

&lt;h2 id=&quot;how-can-convert-help-you&quot;&gt;How can Convert help you?&lt;/h2&gt;

&lt;p&gt;Do you ever want, or need to leverage clixml for handing objects around? Export-Clixml will handle the conversion, except it requires writing to disk, something I won’t do unless I absolutely must. You can use the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.psserializer&quot;&gt;System.Management.Automation.PSSerializer&lt;/a&gt; Namespace to perform the conversion in-memory, but I always forget the Namespace. With Convert, I only need to remember &lt;a href=&quot;https://convert.readthedocs.io/en/latest/functions/ConvertTo-Clixml/&quot;&gt;ConvertTo-Clixml&lt;/a&gt; and &lt;a href=&quot;https://convert.readthedocs.io/en/latest/functions/ConvertFrom-Clixml/&quot;&gt;ConvertFrom-Clixml&lt;/a&gt;, both of which follow the standard “ConvertTo” and “ConvertFrom” naming convention for object conversions.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;PS &lt;/span&gt;C:\&amp;gt; &lt;span class=&quot;s1&quot;&gt;'This is an object'&lt;/span&gt; | ConvertTo-Clixml

&amp;lt;Objs &lt;span class=&quot;nv&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1.1.0.1&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://schemas.microsoft.com/powershell/2004/04&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
  &amp;lt;S&amp;gt;This is an &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&amp;lt;/S&amp;gt;
&amp;lt;/Objs&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What about some other examples where Convert may help?&lt;/p&gt;

&lt;h2 id=&quot;passing-objects-between-disconnected-systems-with-aws-systems-manager-parameter-store&quot;&gt;Passing objects between disconnected systems with AWS Systems Manager, Parameter Store&lt;/h2&gt;

&lt;p&gt;Let’s say you have a process that invokes some PowerShell on a remote system, and you want to save a PowerShell Object somewhere for future retrieval, through a log file, database entry, or some other form of storage. Normally you would need to hunt down the code to do so, or write to the filesystem. Here’s an example using Convert and the &lt;a href=&quot;https://aws.amazon.com/systems-manager/features/#Parameter_Store&quot;&gt;AWS Systems Manager, Parameter Store&lt;/a&gt; to make this easy.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# On the remote system, save the 'Convert' module specification to AWS Parameter Store:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$base64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Get-Module -Name &lt;span class=&quot;s1&quot;&gt;'Convert'&lt;/span&gt; | ConvertTo-Clixml | ConvertTo-Base64 -Compress
&lt;span class=&quot;nb&quot;&gt;Write&lt;/span&gt;-SSMParameter -Name &lt;span class=&quot;s1&quot;&gt;'ConvertModuleSpec'&lt;/span&gt; -Value &lt;span class=&quot;nv&quot;&gt;$base64&lt;/span&gt; -Type &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# On a different system, retrieve the object back in its original form:&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Get-SSMParameterValue -Name &lt;span class=&quot;s1&quot;&gt;'ConvertModuleSpec'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Parameters.Value | ConvertTo-String -Decompress | ConvertFrom-Clixml | &lt;span class=&quot;nb&quot;&gt;Format-Table&lt;/span&gt; -AutoSize

ModuleType Version  Name    ExportedCommands
---------- -------  ----    ----------------
Script     0.1.1.13 convert &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;ConvertTo-String, ConvertFrom-MemoryStreamToString, ConvertTo-Clixml, ConvertFrom-MemoryStreamToBase64...&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;convert-the-output-of-an-aws-lambda-function&quot;&gt;Convert the output of an AWS Lambda Function&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/powershell/latest/reference/items/Invoke-LMFunction.html&quot;&gt;Invoke-LMFunction&lt;/a&gt; PowerShell cmdlet (from &lt;a href=&quot;https://www.powershellgallery.com/packages/AWSPowerShell&quot;&gt;AWSPowerShell&lt;/a&gt; or &lt;a href=&quot;https://www.powershellgallery.com/packages/AWSPowerShell.NetCore&quot;&gt;AWSPowerShell.NetCore&lt;/a&gt;) can be used to invoke an AWS Lambda Function. The invocation itself is easy, however the output is returned as a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.io.memorystream(v=vs.110).aspx&quot;&gt;System.IO.MemoryStream&lt;/a&gt; object. Converting a MemoryStream to a String is a common process for Software Developers, but most PowerShell Developers won’t know this off the top of their head. With Convert this is easy; simply pipe the Payload property to &lt;a href=&quot;http://convert.readthedocs.io/en/latest/functions/ConvertTo-String/&quot;&gt;ConvertTo-String&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ConvertTo-Json&lt;/span&gt; -Compress -InputObject @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'name'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Andrew'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Invoke-LMFunction -FunctionName &lt;span class=&quot;s1&quot;&gt;'GetHelloWorld'&lt;/span&gt; -Payload &lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Payload | ConvertTo-String

&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;Andrew&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To take this one step further, pipe the output to ConvertFrom-Json to create a PowerShell object to work with.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Invoke-LMFunction -FunctionName &lt;span class=&quot;s1&quot;&gt;'GetHelloWorld'&lt;/span&gt; -Payload &lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Payload | ConvertTo-String | &lt;span class=&quot;nb&quot;&gt;ConvertFrom-Json

&lt;/span&gt;Hello
-----
Andrew
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;stream-a-powershell-object-to-amazon-simple-storage-service-s3&quot;&gt;Stream a PowerShell object to Amazon Simple Storage Service (S3)&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/powershell/latest/reference/items/Write-S3Object.html&quot;&gt;Write-S3Object&lt;/a&gt; PowerShell cmdlet (also from AWSPowerShell or AWSPowerShell.NetCore) is used to upload an object to S3. Common use-cases are to upload files and folders from your local filesystem, however the cmdlet also supports uploading data from a MemoryStream. Convert enables this with in-memory conversions from any object, to a MemoryStream object. Let’s see how.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$memoryStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Get-Module -Name &lt;span class=&quot;s1&quot;&gt;'Convert'&lt;/span&gt; | ConvertTo-Clixml | ConvertTo-MemoryStream
&lt;span class=&quot;nb&quot;&gt;Write&lt;/span&gt;-S3Object -BucketName &lt;span class=&quot;s1&quot;&gt;'mys3bucket'&lt;/span&gt; -Key &lt;span class=&quot;s1&quot;&gt;'ConvertModuleSpec.clixml'&lt;/span&gt; -Stream &lt;span class=&quot;nv&quot;&gt;$memoryStream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So there you have it, I hope I’ve outlined why I released Convert to the PowerShell Gallery. It is available for installation today, and you can view the source code on &lt;a href=&quot;https://github.com/austoonz/Convert&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 31 May 2018 22:15:00 +0000</pubDate>
        <link>https://andrewpearce.io//powershell/2018/05/31/announcing-convert/</link>
        <guid isPermaLink="true">https://andrewpearce.io//powershell/2018/05/31/announcing-convert/</guid>
        
        <category>powershell</category>
        
        
        <category>powershell</category>
        
      </item>
    
  </channel>
</rss>
