<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeremy Davis</title>
    <description>The latest articles on DEV Community by Jeremy Davis (@jermdavis).</description>
    <link>https://dev.to/jermdavis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4199%2Ffc0876ea-daf4-47dc-87a5-e2a840d13f09.jpg</url>
      <title>DEV Community: Jeremy Davis</title>
      <link>https://dev.to/jermdavis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jermdavis"/>
    <language>en</language>
    <item>
      <title>Reformatting config XML so it’s easier to diff</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 30 Sep 2019 07:22:37 +0000</pubDate>
      <link>https://dev.to/jermdavis/reformatting-config-xml-so-it-s-easier-to-diff-3den</link>
      <guid>https://dev.to/jermdavis/reformatting-config-xml-so-it-s-easier-to-diff-3den</guid>
      <description>&lt;p&gt;Every so often pretty much every developer ends up in a situation where they’re looking at a bug that manifests on one platform, but not on another. The sort of bug where you end up spending hours looking through log and config files for a subtle difference. I found myself looking into just this sort of bug recently, but on a site where (to my frustration) the config files were full of comments and whitespace differences across platforms that made diffing really hard**. Spotting that subtle bug-causing difference is pretty much impossible when your diff is full of noise… So how can we fix that?&lt;/p&gt;

&lt;p&gt;With some Powershell of course!&lt;/p&gt;

&lt;p&gt;I realised life would be much simpler for me if I could standardise the formatting and drop all the comments from the config. That way any differences that the diff tool showed up would be actual configuration differences, and not any of the annoying noise. So I hacked up some code to iterate a tree of &lt;code&gt;.config&lt;/code&gt; files and reformat them for me…&lt;/p&gt;

&lt;p&gt;Via the &lt;code&gt;System.Xml.XmlDocument&lt;/code&gt; object we can load a config file while ignoring comments, and then write it back to disk with standardised indenting with a function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tidyConfigFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Modifying &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Xml.XmlReaderSettings&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IgnoreComments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$reader&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Xml.XmlReader&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Xml.XmlDocument&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$xml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$reader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$xml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing of note there is that you need to use an &lt;code&gt;XmlReader&lt;/code&gt; with special settings to get the &lt;code&gt;XmlDocument&lt;/code&gt; to strip out comments when it’s loading.&lt;/p&gt;

&lt;p&gt;So that will take some messy xml like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- hello --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;alpha&amp;gt;&lt;/span&gt;



&lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/alpha&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and neaten it up to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;alpha&amp;gt;
  &amp;lt;test x="y" /&amp;gt;
&amp;lt;/alpha&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So little recursive function to step through a tree of folders updating any config files will sort out the rest of the task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processFolder&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Processing &lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExpandProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FullName&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;tidyConfigFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExpandProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FullName&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$child&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;processFolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$child&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with that in place, I could just put copies of my two sets of config in one folder, and let the script go…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/copiedconfig.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cvRFh6wo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/copiedconfig.png%3Fw%3D800%26h%3D450" alt="" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;processFolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\CopiedConfig"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get myself a set of files that were much easier to diff.&lt;/p&gt;

&lt;p&gt;Simple…&lt;/p&gt;




&lt;p&gt;** Yes, I know full well the fact there are differences like this is a massive code smell. But sometimes when you're the support developer on projects with little budget you just have to hold your nose and make it work, rather than spending days reworking the entre deployment process...&lt;/p&gt;

</description>
      <category>configuration</category>
      <category>powershell</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>Caching when you have duplicate container components</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 16 Sep 2019 07:10:33 +0000</pubDate>
      <link>https://dev.to/jermdavis/caching-when-you-have-duplicate-container-components-26m8</link>
      <guid>https://dev.to/jermdavis/caching-when-you-have-duplicate-container-components-26m8</guid>
      <description>&lt;p&gt;In theory, the magic of Dynamic Placeholders lets us have a container component placed onto your page more than once. That didn’t work in the old world of “static” placeholders, because the rendering engine didn’t like two placeholders with the same name. But despite it’s benefits, the dynamic implementation has an annoying edge case – you may not be able to enable caching for your container component. I had a client bump into this issue recently, so I spent some time considering approaches that might help them address this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;Imagine you have a container component. A very simple one will do fine – all it really needs to contain is a Dynamic Placeholder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@using Sitecore.Mvc
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"border:2px solid red; margin-top:10px; padding:10px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Dynamic Container&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    @Html.Sitecore().DynamicPlaceholder("container")
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you build up a page using two instances of that component, with some child components inside each of the placeholders it will look fine in Experience Editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/creatingcontainers.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MLAgMhTo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/creatingcontainers.png%3Fw%3D800%26h%3D597" alt="" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But if the container components have caching enabled, it won’t look right on the public site:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/wrongui.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--knE91BKm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/wrongui.png%3Fw%3D800%26h%3D598" alt="" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s because the code renders the first container and caches the result. Then it starts to render the second one, but gets a cache hit, and so returns the cached data for the first one. In the cache you see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/incorrectcaches.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9vMwMPe4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/incorrectcaches.png%3Fw%3D800%26h%3D598" alt="" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s no cache variant that works for “this component varies by its children” – so only the first instance of the container ever gets rendered and cached.&lt;/p&gt;

&lt;p&gt;My interest in this came about because I got handed a bug report on a client site that said “our page is wrong!” and it turned out to be because of this issue. I think there are there main ways that the issue could be resolved to make the client happy:&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution one: Just disable caching
&lt;/h2&gt;

&lt;p&gt;If you turn the cache settings off for the container component, all will look right.&lt;/p&gt;

&lt;p&gt;That’s probably fine if your container has few children, or it’s not used very much. The performance difference in that scenario is likely to be small – especially if you are able to enable caching for the child components inside your container.&lt;/p&gt;

&lt;p&gt;That’s a fix developers can roll out easily (it’s just undoing the cache settings on the rendering item) and it doesn’t affect editors at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution two: Use a fake data source
&lt;/h2&gt;

&lt;p&gt;You could configure the container component with the “vary by datasource” cache setting instead of turning caching off. If the component doesn’t really need a data source, the code will just ignore what ever item the datasource setting points to, other than for caching. And that can allow the normal caching framework to work here.&lt;/p&gt;

&lt;p&gt;No deployment is required for this, which is a bonus. But it might be confusing for editors to need to add a data source where it’s not really needed. Plus this doesn’t work if your container does need a datasource, and you have two on a page that need the same datasource but different children.&lt;/p&gt;

&lt;p&gt;So this might not be a great answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution three: Vary the cache by rendering id
&lt;/h3&gt;

&lt;p&gt;Finally, it turns out it’s not actually that hard to extend Sitecore’s caching model to allow for the idea of “cache this specific rendering instance”. You could write some code that looked at the actual children of a component and generated a cache key based on that. Maybe by hashing some data from the Rendering XML? But it struck me that it’s faster and easier (and probably much the same outcome) to just make use of the unique ID that every instance of a rendering gets by default…&lt;/p&gt;

&lt;p&gt;To do this, you need to create a new data template to store your “cache by rendering’s unique id” flag:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/newcacheflag.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d7as6p13--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/newcacheflag.png%3Fw%3D800%26h%3D570" alt="" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then add that to the inheritance tree of the standard caching flags item:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/extendcachingflags-1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wICnpvxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/extendcachingflags-1.png%3Fw%3D800%26h%3D570" alt="" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that lets you set a caching flag on your container rendering:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/cachesetting.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G-dfHVif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/cachesetting.png%3Fw%3D800%26h%3D570" alt="" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To act on that new flag, you need to deploy a simple bit of code to extend the rendering pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VaryByUniqueIdCacheKey&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sitecore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mvc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pipelines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderRendering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenerateCacheKey&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rendering&lt;/span&gt; &lt;span class="n"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RenderRenderingArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheField&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CheckboxField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"VaryByUniqueId"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"_#uniqueId:"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UniqueId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks for the extra cache variant flag on the item being rendered, and if it exists the cache key is extended with the unique ID for this rendering instance.&lt;/p&gt;

&lt;p&gt;And you can patch that into your config via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class="na"&gt;xmlns:patch=&lt;/span&gt;&lt;span class="s"&gt;"http://www.sitecore.net/xmlconfig/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;sitecore&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pipelines&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;mvc.renderRendering&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;processor&lt;/span&gt; &lt;span class="na"&gt;patch:instead=&lt;/span&gt;&lt;span class="s"&gt;"processor[@type='Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey, Sitecore.Mvc']"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Caching.VaryByUniqueIdCacheKey, Caching"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/mvc.renderRendering&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/pipelines&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/sitecore&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that’s done, you’ll get a separate cache entry for each instance of your container component:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/correctcache.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EUJUm5XE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/correctcache.png%3Fw%3D800%26h%3D598" alt="" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the published version of the site will look correct:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/09/correctoutput-1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pgVPeNwU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/09/correctoutput-1.png%3Fw%3D800%26h%3D598" alt="" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whether that solution is “better” for you than just disabling caching probably depends on the effort involved in rendering each of your containers. If they have one or two items in them, chances are it’s not worth it. But if your container had a larger number of items, the “memory usage vs processing time” trade-off might be worth it.&lt;/p&gt;

&lt;p&gt;It’s an option for you…&lt;/p&gt;

</description>
      <category>bug</category>
      <category>caching</category>
      <category>presentation</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>An edge case of remote events with Publishing Service</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 02 Sep 2019 07:23:21 +0000</pubDate>
      <link>https://dev.to/jermdavis/an-edge-case-of-remote-events-with-publishing-service-5g0n</link>
      <guid>https://dev.to/jermdavis/an-edge-case-of-remote-events-with-publishing-service-5g0n</guid>
      <description>&lt;p&gt;I’ve been working on an international deployment of Sitecore recently, and resolving some problems around how publishing raises remote events has demonstrated that there are some things about the publishing process that I didn’t entirely understand… I doubt this is a common scenario, but it still seems worth writing down what I’ve learned – So here’s another crib sheet for my future self:&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario:
&lt;/h2&gt;

&lt;p&gt;I won’t go into the reasons for why, but this deployment has Sitecore broken up into three bits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A shared Master database that lives in a central location&lt;/li&gt;
&lt;li&gt;A Sitecore cluster in Europe which has all the roles other than the Master DB&lt;/li&gt;
&lt;li&gt;A Sitecore cluster in Australia which has all the roles other than the Master DB&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/07/rolediagram-1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JJ4VTmXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/07/rolediagram-1.png%3Fw%3D800%26h%3D442" alt="" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So authors log in to the Sitecore instance that’s geographically closest to them, and do their editing there. When they’re done they hit “publish” and the changes are sent to Publishing Targets for both the EU and AU web databases.&lt;/p&gt;

&lt;p&gt;Since there’s a WAN link between the central Master DB and the local Web DBs, this seemed like a use-case for Publishing Service, as it’s much faster processing should be much better at dealing with the network latency involved in this scenario.&lt;/p&gt;

&lt;p&gt;And mostly this works ok…&lt;/p&gt;

&lt;p&gt;But it was noticable that when an author published in Europe, it updated fine, but caches and indexes did not update in Australia. And that made sense, as checking under the surface the Events tables in the Australian databases did not get any “a publish has happened” messages written to them…&lt;/p&gt;

&lt;p&gt;So clearly I got something wrong – and after failing to fix it myself I spent some time discussing the scenario with Sitecore Support.&lt;/p&gt;

&lt;h2&gt;
  
  
  My bad assumptions…
&lt;/h2&gt;

&lt;p&gt;Initially I looked at the role diagram for the site, and thought that Publishing Service instances need to know about both the EU and AU databases, but they are the only bits of the system that need to.&lt;/p&gt;

&lt;p&gt;So Publishing Service can be configured to see the “web” Publishing Target databases in EU and AU, plus the central Master. But the CM and CD instances only care about their local web database and Master. So they don’t need to know about the remote web databases at all.&lt;/p&gt;

&lt;p&gt;That seems logical – and the publish operations largely work. But the “events don’t happen remotely” thing suggested it wasn’t right.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned from support…
&lt;/h2&gt;

&lt;p&gt;So it turns out that the “a publish has happened” events are not actually generated by Publishing Service. It does the work of moving items between databases, but it doesn’t do the work of triggering all the events. That work still happens inside the CM instance.&lt;/p&gt;

&lt;p&gt;That means that this scenario requires you have to have all your remote databases configured as Publishing Targets for both the CM role and for Publishing Service.&lt;/p&gt;

&lt;p&gt;So fix my issue, I had to change the configuration so that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All of the web databases appear in the CM server connection string files with unique names. e.g. naming them “web_europe” and “web_australia”&lt;/li&gt;
&lt;li&gt;All of the web databases need to be &lt;a href="https://tothecore.sk/2018/05/10/adding-publishing-targets-in-sitecore-9/"&gt;configured as Publishing Targets in both of the CM instance&lt;/a&gt;, using these unique names. This requires both a config patch to configure the event queues etc, and making sure there are the right items under “/system/Publishing Targets” in Master that define these publishing targets, using the unique database names.&lt;/li&gt;
&lt;li&gt;The  definitions on the CM box need to have their “database” attributes updated to use the unique database names.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://grantkillian.wordpress.com/2018/12/17/how-i-add-custom-sitecore-publishing-service-targets/"&gt;configuration for the target databases in Publishing Service&lt;/a&gt; must use the same unqiue names, and must specify the unique database names for each target. (So Publishing Service needs these databases in its connection strings file too)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once both Publishing Service and all of the CM boxes knew about all of the databases, the events started to appear in the remote database after a publish, and magically caches cleared and indexes updated after each publish.&lt;/p&gt;

&lt;p&gt;You don’t appear to need to change the CD server config to match the CM changes.&lt;/p&gt;

&lt;p&gt;Once again, Sitecore Support saves my bacon – &lt;a href="https://media.giphy.com/media/4EF5xIO5yiivWh4gGn/giphy.gif"&gt;Thank you&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>publishing</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>Clearing up a little confusion with Let’s Encrypt’s DNS challenges</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 19 Aug 2019 08:00:48 +0000</pubDate>
      <link>https://dev.to/jermdavis/clearing-up-a-little-confusion-with-let-s-encrypt-s-dns-challenges-216h</link>
      <guid>https://dev.to/jermdavis/clearing-up-a-little-confusion-with-let-s-encrypt-s-dns-challenges-216h</guid>
      <description>&lt;p&gt;I love &lt;a href="https://letsencrypt.org/"&gt;Let’s Encrypt&lt;/a&gt;. I’ve been using SSL for all my personal projects for years. Until they came along I was using self-signed certificates and manually adding my own root certificate to all the machines I was using my sites from. And &lt;a href="https://jermdavis.wordpress.com/2016/10/31/banging-my-head-against-git-visual-studio-15-and-ssl/"&gt;that lead to some fun&lt;/a&gt;… So I moved eagerly to Let’s Encrypt when the tooling supported Windows reasonably well, and set myself up with a certificate with multiple &lt;a href="https://en.wikipedia.org/wiki/Subject_Alternative_Name"&gt;SANs&lt;/a&gt; authenticated via their “HTTP proofs” mechanism, and it all worked fine, despite it being a bit of a pain that I had to expose port 80 for sites I only wanted accessible via port 443.&lt;/p&gt;

&lt;p&gt;But I realised recently that they now offer wildcard certs that would make my life simpler, and that there is now decent support for DNS-based proof-of-ownership. So recently I tried moving my server over to this model – and there was a bit of friction. Entirely &lt;a href="https://en.wiktionary.org/wiki/PEBCAK"&gt;PEBCAK&lt;/a&gt; though – so I’m writing this down for the next time I forget how DNS works &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a user of Windows servers, the &lt;a href="https://certifytheweb.com/"&gt;Certify&lt;/a&gt; client is the easiest way to set up this sort of thing. So I started by downloading the latest instance of that – which worked easily enough.&lt;/p&gt;

&lt;p&gt;From there you can request a new certificate for “*.yourdomain.com”. You also have to tell it that you’re using a DNS challenge. Depending on your DNS provider, you may be lucky enough to have support for automatically updating DNS with the challenge data. That worked for me with one domain, which was hosted via &lt;a href="https://www.cloudflare.com/"&gt;CloudFlare&lt;/a&gt;. But it wasn’t available for me with another domain – which is where I had a little confusion.&lt;/p&gt;

&lt;p&gt;If automation isn’t supported for your DNS provider you have to select the “manual” update mechanism… Then, when you request the certificate, Certify tells you about the TXT record(s) you need to create. You get something like this from the UI and logs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/08/certify.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LkcIvMsR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/08/certify.png%3Fw%3D800%26h%3D510" alt="" width="800" height="510"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2019-06-22 14:24:06.261 +01:00 [INF] DNS: (Update DNS Manually) :: Please login to your DNS control panel for the domain '\*.mydomain.co.uk' and create a new TXT record named: \_acme-challenge.mydomain.co.uk with the value:a-ELrRGO72uAYDPxb8YpyzDrHkC8n71Yc67sW7HX1bX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you log in to your DNS provider, and copy/paste these values into a TXT record…&lt;/p&gt;

&lt;h2&gt;
  
  
  Except no…
&lt;/h2&gt;

&lt;p&gt;Don’t make the silly mistake I did intially – remember how DNS works…&lt;/p&gt;

&lt;p&gt;You do not log in to your DNS portal and paste “_acme-challenge.mydomain.co.uk” into a new TXT record’s Name field – you create a TXT record named “_acme-challenge” under your domain “mydomain.co.uk”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/08/dns.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j9edqx97--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/08/dns.png%3Fw%3D800%26h%3D564" alt="" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems very obvious in retrospect, but it this caught me out because it seems some DNS providers will recognise the mistake here, and strip the DNS suffix off the name of your new TXT record. Cloudflare, for example, are wise to this mistake. But some will not – including my other provider.&lt;/p&gt;

&lt;p&gt;And if you do make this copy/paste mistake the Let’s Encrypt validation will not work, because it will not be able to find the “right” TXT record. And then you’ll spend half an hour carefully comparing the challenge value, wondering if you got that random string of nonsense wrong…&lt;/p&gt;

&lt;p&gt;Must remember not to make this mistake next time &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dns</category>
    </item>
    <item>
      <title>Another Marketing Automation gotcha</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 22 Jul 2019 07:30:05 +0000</pubDate>
      <link>https://dev.to/jermdavis/another-marketing-automation-gotcha-56a5</link>
      <guid>https://dev.to/jermdavis/another-marketing-automation-gotcha-56a5</guid>
      <description>&lt;p&gt;Following on from &lt;a href="https://dev.to/jermdavis/shooting-myself-in-the-foot-with-marketing-automation-31o3-temp-slug-3025341"&gt;my recent post about how I was able to mess up my life by getting Marketing Automation connection strings wrong&lt;/a&gt;, I hit another interesting issue with MA – this time around content languages…&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;Looking at a newly deployed instance of Sitecore, my editor account couldn’t see any Campaign Templates in the Marketing Automation dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/empty-ma-dashboard.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F06%2Fempty-ma-dashboard.png%3Fw%3D800%26h%3D492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That wasn’t right, as I could see the template items sitting in Content Editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/ma-templates.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F06%2Fma-templates.png%3Fw%3D800%26h%3D492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing a bit of digging, I noticed that that dasboard page was making a web service call that I could examine via browser dev tools:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/ma-fetchapi-2.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F06%2Fma-fetchapi-2.png%3Fw%3D800%26h%3D492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The request was passing in a querystring value: “&lt;code&gt;cultureName=fr-FR&lt;/code&gt;” And the result of that call was a valid json response for no data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if I changed that url to use “en” as its language, the data changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"938ec74b-3ba4-4c93-9152-015655088998"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"938ec74b-3ba4-4c93-9152-015655088998"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cart Abandonment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"endDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createdBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sitecore&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createdDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-08-17T12:26:31Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cultureName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customValues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"When a customer fails to complete an order, an automated marketing email message  is sent to the recipient. The intention is to re-engage the recipient and to encourage them to complete the order. Appropriate timings differ depending on the sales cycle of the product. In our example we show three email messages over three days. To discourage misuse of promotional codes in this campaign, brands may want to set a limit to the number of times a recipient can re-enter this campaign."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inactive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lastModifiedBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lastModifiedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-10-05T06:54:16Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"classifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currentEnrollments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"marketingThemeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"85506484-7854-4b14-ab0c-dfc61ca65cc0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxEnrollmentsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;snip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ae5178d0-9484-41f3-8c9e-cb1b8f478e24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"endDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createdBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sitecore&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createdDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-04-03T04:07:59Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cultureName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customValues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inactive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lastModifiedBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lastModifiedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-04-03T04:09:06Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"classifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currentEnrollments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"marketingThemeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxEnrollmentsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the issue is something to do with languages…&lt;/p&gt;

&lt;h2&gt;
  
  
  Working around the issue
&lt;/h2&gt;

&lt;p&gt;Thinking about this, I tried looking at the user’s settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/user-language.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F06%2Fuser-language.png%3Fw%3D800%26h%3D492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And I realised that these users who were having problems were set to have a specific default content language to match the client’s project requiremenents. If I change the user back to having the “default” or “en” then the Marketing Automation dashboard starts to work correctly again – which would fit with the behaviour seen by changing the url above.&lt;/p&gt;

&lt;p&gt;A colleage suggested translating the Marketing Automation template items into the client’s required language might make a good workaround, so I tried that approach. Any items from the templates folder above that you translate start to appear in the templates view. So you can use the “create from template” option to try and make a new Marketing Automation flow. But unfortunately when the editor loads up you still get an error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/brokentemplate.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F06%2Fbrokentemplate.png%3Fw%3D800%26h%3D492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the logs, you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 728 20:37:07 ERROR [Sitecore Services]: HTTP GET
URL http://sc911.sc/sitecore/api/ma/automationcampaigntemplates/422e238c-65aa-4ec9-8f98-d563cdee6c72?cultureName=fr-FR&amp;amp;sc_site=shell

Exception System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure "Sitecore.Xdb.MarketingAutomation.Locators.Xmgmt.Resources.resources" was correctly embedded or linked into assembly "Sitecore.Xdb.MarketingAutomation.Locators.Xmgmt" at compile time, or that all the satellite assemblies required are loadable and fully signed.
   at System.Resources.ManifestBasedResourceGroveler.HandleResourceStreamMissing(String fileName)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(CultureInfo culture, Dictionary`2 localResourceSets, Boolean tryParents, Boolean createIfNotExists, StackCrawlMark&amp;amp; stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark&amp;amp; stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
   at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)
   at Sitecore.Xdb.MarketingAutomation.Locators.Xmgmt.AutomationPlans.ItemActivityDescriptorLocator.ToActivityDescriptor(ActivityTypeItem activityTypeItem, CultureInfo culture)
   at Sitecore.Xdb.MarketingAutomation.Locators.Xmgmt.AutomationPlans.ItemActivityDescriptorLocator.GetDescriptor(Guid id, CultureInfo culture)
   at Sitecore.Marketing.Automation.Extensions.AutomationActivityDefinitionViewModelExtensions.ToViewModel(IAutomationActivityDefinition automationActivityDefinition, IActivityDescriptorLocator activityDescriptorLocator, CultureInfo culture, IActivityConverterFactory`1 activityConverterFactory, BaseLog logger)
   at Sitecore.Marketing.Automation.Extensions.AutomationPlanDefinitionViewModelExtensions.&amp;lt;&amp;gt;c__DisplayClass0_0.&amp;lt;ToViewModel&amp;gt;b__2(IAutomationActivityDefinition x)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at Sitecore.Marketing.Automation.Extensions.AutomationPlanDefinitionViewModelExtensions.ToViewModel(IAutomationPlanDefinition automationPlanDefinition, IActivityDescriptorLocator activityDescriptorLocator, CultureInfo culture, IActivityConverterFactory`1 activityConverterFactory, BaseLog logger)
   at Sitecore.Marketing.Automation.Data.AutomationCampaignTemplateRepository.GetById(Guid id, String cultureName)
   at Sitecore.Marketing.Automation.Client.Controllers.AutomationCampaignTemplatesController.&amp;lt;GetById&amp;gt;d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.TaskHelpersExtensions.&amp;lt;CastToObject&amp;gt;d__1`1.MoveNext()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That reads like there’s a binary which does not have resource file data for the French language – and hence it’s not going to work.&lt;/p&gt;

&lt;p&gt;However if I do switch the user back to having their default language be “en” then this does work… So it looks like users who want to modify automation flows need to make sure they have a default content language set “en” while they’re working…&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>Shooting myself in the foot with Marketing Automation</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 08 Jul 2019 08:05:30 +0000</pubDate>
      <link>https://dev.to/jermdavis/shooting-myself-in-the-foot-with-marketing-automation-4oae</link>
      <guid>https://dev.to/jermdavis/shooting-myself-in-the-foot-with-marketing-automation-4oae</guid>
      <description>&lt;p&gt;I had another of my fun chats with Sitecore Support recently, for an issue that seemed to get no useful results in Google when I looked. So, as is my way, I’m filling that search-engine void today. This turned out to be entirely my fault – but it seems like the sort of mistake that others might encounter too… So if you’ve deployed a distributed instance of Sitecore and found Marketing Automation was behaving oddly, read on…&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;I’ve been working on a large deployment of Sitecore v9.1 Update 1, using the XP1 SIF scripts. Mostly this has been pretty pain-free. I was able to edit the assorted json files to fill in the specific bits of configuration I needed for my deployment, and then run the right scripts in the right places to make it all work.&lt;/p&gt;

&lt;p&gt;But after the initial deployment I noticed that one aspect of the site was not working correctly. When you tried to browse to the Marketing Automation UI, you’d get a pause and then a cryptic error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/06/marketingautomationerror.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dgmMF84s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/06/marketingautomationerror.png%3Fw%3D800%26h%3D313" alt="" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Digging into the Sitecore logs on my CM server, I found this error, sitting on top of a very long stack trace. I’ve trimmed off the end, as it just repeats much the same thing for a long time, but the interesting bit was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;13976 14:53:30 ERROR [Sitecore Services]: HTTP GET
URL https://author.redacted.com/sitecore/api/ma/plans/?cultureName=en-GB&amp;amp;sc_site=shell

Exception System.InvalidOperationException: Get plan statistics did not complete successfully. StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Connection: close
  Date: Mon, 03 Jun 2019 12:53:30 GMT
  Server: Microsoft-HTTPAPI/2.0
  Content-Length: 315
  Content-Type: text/html; charset=us-ascii
}
   at Sitecore.Xdb.Common.Web.Synchronous.SynchronousExtensions.SuspendContextLock[TResult](Func`1 taskFactory)
   at Sitecore.Marketing.Automation.Data.AutomationPlanRepository.&amp;lt;GetAll&amp;gt;d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Sitecore.Marketing.Automation.Client.Controllers.AutomationPlansController.&amp;lt;GetAll&amp;gt;d__8.MoveNext()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There was nothing in the Marketing Automation service’s logs to indicate an error at their end, and the IIS logs had this request on the CM server, but with a 500 status code rather than the 404 implied above.&lt;/p&gt;

&lt;p&gt;Google didn’t help me, so after a bit of head scratching I raised a support ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;Having looked in more detail at all the config for my CM and Marketing Automation instances, support pointed out an obvious issue: The connection strings file on my CM server had the URL for Marketing Automation as an HTTP endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;connectionStrings&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- 
    Sitecore connection strings.
    All database connections for Sitecore are configured here.
  --&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;add&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xdb.marketingautomation.operations.client"&lt;/span&gt; &lt;span class="na"&gt;connectionString=&lt;/span&gt;&lt;span class="s"&gt;"http://xma.redacted.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;add&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xdb.marketingautomation.reporting.client"&lt;/span&gt; &lt;span class="na"&gt;connectionString=&lt;/span&gt;&lt;span class="s"&gt;"http://xmareporting.redacted.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/connectionStrings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the scripts which set up the services behind those endpoints only gave them HTTPS bindings – becasue xConnect services are secure-by-default. So that was clearly causing a failure when the CM instance tried to talk to the Marketing Automation services and got no response from the remote server, because it had no HTTP binding.&lt;/p&gt;

&lt;p&gt;A bit of further digging &lt;a href="https://media.giphy.com/media/XsUtdIeJ0MWMo/giphy.mp4"&gt;lead to the embarrasing discovery&lt;/a&gt; that I’d managed to typo the Marketing Automation URL in the CM server SIF json file. Somehow I’d managed to use “http” instead of “https” there, and hence the CM server was set up to use the wrong URL.&lt;/p&gt;

&lt;p&gt;So don’t be me. Proof-read your SIF scripts carefully before you deploy…&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>bug</category>
      <category>installation</category>
      <category>sif</category>
    </item>
    <item>
      <title>Pay attention to your index exclusions</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Thu, 04 Jul 2019 08:24:15 +0000</pubDate>
      <link>https://dev.to/jermdavis/pay-attention-to-your-index-exclusions-4fgb</link>
      <guid>https://dev.to/jermdavis/pay-attention-to-your-index-exclusions-4fgb</guid>
      <description>&lt;p&gt;I hit an interesting issue recently: Some code that worked fine on a QA instance of Sitecore had been deployed for UAT and was now failing with an odd error message. Whilst this issue was entirely our fault, there wasn’t much in Google about the error messages I was seeing, so I’m trying to correct that problem today…&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;The code in question was runing on a Sitecore 7 instance. It fell over on UAT with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ArgumentNullException: Value cannot be null.Parameter name: key]
  System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) +49
  System.Collections.Generic.Dictionary`2.FindEntry(TKey key) +14545882
  System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue&amp;amp; value) +20
  Sitecore.ContentSearch.ContentSearchManager.GetIndex(String name) +52
  Sitecore.ContentSearch.ContentSearchManager.CreateSearchContext(IIndexable indexable) +17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s not a very helpful error, so I spent some time tracking it down. From the stack trace you can see that the problem seems to be in translating an &lt;code&gt;IIndexable&lt;/code&gt; into a search context, but it doesn’t say anything helpful about why…&lt;/p&gt;

&lt;p&gt;And under the surface, the code which was crashing looked something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sitecore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{08D9E0A1-BB72-48FD-AAB2-EFCD6F3B3C92}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ContentSearchManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateSearchContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SitecoreIndexableItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// run some search queries starting from the "root" item...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The cause
&lt;/h2&gt;

&lt;p&gt;A bit of digging with my old friend ILSpy lead to me working out that the exception being thrown here is because &lt;code&gt;CreateSearchContext()&lt;/code&gt; above does two things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IProviderSearchContext&lt;/span&gt; &lt;span class="nf"&gt;CreateSearchContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IIndexable&lt;/span&gt; &lt;span class="n"&gt;indexable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;GetIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexable&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;CreateSearchContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchSecurityOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnableSecurityCheck&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It translates the &lt;code&gt;IIndexable&lt;/code&gt; into a valid index for that item. And then it creates a search context on that index.&lt;/p&gt;

&lt;p&gt;The exception gets thrown because the translation returns null – that item doesn’t map to any index. Under the surface, there’s a pipeline that gets run to do this translation, and it iterates all the defined indexes. And asks them in turn if they contain the item in question.&lt;/p&gt;

&lt;p&gt;In the scenario I was looking at, all indexes returned “no” when asked this, and the code in the pipeline returns null when that happens… So later on when Sitecore tries to fetch its index by looking up the index based on a null name, you get the exception above.&lt;/p&gt;

&lt;p&gt;Some further digging found a key difference between the configurations of the “working” servers and the “broken” one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the machines where it worked Sitecore’s search indexes were configured as “index all templates, except some specific ones”&lt;/li&gt;
&lt;li&gt;But the servers that were broken were configured as “exclude all templates, except some specific ones”…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real cause of this issue was that the Sitecore Item passed into &lt;code&gt;ContentSearchManager.CreateSearchContext()&lt;/code&gt; was based on a template that was specifically excluded from search indexing. As soon as I fixed this, the code started working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Well, the obvious conclusions are: 1) Don’t exclude items you’re going to call &lt;code&gt;ContentSearchManager.CreateSearchContext()&lt;/code&gt; on from your indexes. And 2) Manage your configuration across deployments better than this… &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having done some further testing, the error message above seems specific to older versions of Sitecore. I’ve tested this on V9.1 as well, and it still crashes in this scenario, but it shows a different exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[NullReferenceException: Object reference not set to an instance of an object.] 

 Sitecore.ContentSearch.SitecoreItemCrawler.IsExcludedFromIndex(SitecoreIndexableItem indexable, Boolean checkLocation) +69
  Sitecore.ContentSearch.SitecoreItemCrawler.GetContextIndexRanking(IIndexable indexable) +113
  System.Linq.WhereSelectListIterator`2.MoveNext() +116
  System.Linq.Enumerable.Min(IEnumerable`1 source) +72

 Sitecore.ContentSearch.AbstractSearchIndex.Sitecore.ContentSearch.Pipelines.GetContextIndex.IContextIndexRankable.GetContextIndexRanking(IIndexable indexable) +119
  Sitecore.ContentSearch.Pipelines.GetContextIndex.&amp;lt;&amp;gt;c\_\_DisplayClass6\_0.&amp;lt;RankContextIndexes&amp;gt;b\_\_0(ISearchIndex i) +71
  System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +237
  System.Linq.Buffer`1..ctor(IEnumerable`1 source) +280
  System.Linq.&amp;lt;GetEnumerator&amp;gt;d__1.MoveNext() +115
  System.Linq.Buffer`1..ctor(IEnumerable`1 source) +280
  System.Linq.Enumerable.ToArray(IEnumerable`1 source) +89

 Sitecore.ContentSearch.Pipelines.GetContextIndex.FetchIndex.GetContextIndex(IIndexable indexable, GetContextIndexArgs args) +699

 Sitecore.ContentSearch.Pipelines.GetContextIndex.FetchIndex.Process(GetContextIndexArgs args) +48 (Object , Object ) +13
  Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +483
  Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists) +235
  Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain) +21
  Sitecore.Abstractions.CorePipelineWrapper.Run(String pipelineName, PipelineArgs args) +73

 Sitecore.ContentSearch.Pipelines.GetContextIndex.GetContextIndexPipeline.Run(ICorePipeline pipeline, GetContextIndexArgs args) +38
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trace is a bit more obvious – but for people new to Sitecore’s index configuration process that still might be confusing. So if you’re seeing either of the error messages shown above, check what you’re excluding from your indexes…&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>solr</category>
    </item>
    <item>
      <title>Logging generated passwords in SIF</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 24 Jun 2019 08:23:52 +0000</pubDate>
      <link>https://dev.to/jermdavis/logging-generated-passwords-in-sif-562p</link>
      <guid>https://dev.to/jermdavis/logging-generated-passwords-in-sif-562p</guid>
      <description>&lt;p&gt;I’ve been looking at adjusting SIF scripts for a production deployment recently, and realised that sometimes you’d like SIF to generate random passwords for you, but you need them logged so you can reuse them in scripts you’re crafting for other roles. It doesn’t do that out of the box, but it turns out it’s actually quite simple:&lt;/p&gt;

&lt;p&gt;The default configuration for “&lt;code&gt;XPx-SingleDeveloper.json&lt;/code&gt;” includes a task that generates a set of random passwords (and some other stuff) for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GeneratePasswords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Generates all shared passwords and secrets.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SetVariable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Params&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XP1Passwords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Global&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="cm"&gt;/* Other generation tasks... */&lt;/span&gt;

                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SqlCorePassword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[variable('SqlCore.Password')]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

                    &lt;span class="cm"&gt;/* Other generation tasks... */&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you’re feeling too lazy to generate all these things yourself, it seems sensible to reuse this in scripts where everything’s not on the same box…&lt;/p&gt;

&lt;p&gt;It squirrels all these values away into variables, so that they can be reused for all the included tasks that set up all the roles – but it doesn’t seem to record them anywhere else. That’s entirely sensible from a security perspective – but that’s of no help if you’re going to have to take the Content Delivery &lt;code&gt;.json&lt;/code&gt; over to another server and run it there. (Yes, I know SIF v2 supports remoting – but so far I’ve yet to meet a client that actually allows that on their production servers)&lt;/p&gt;

&lt;p&gt;But it turns out that it’s actually very simple to get SIF to log something for you. There are two things to do:&lt;/p&gt;

&lt;p&gt;First, you need to ensure that you’ve registered the “&lt;code&gt;WriteInformation&lt;/code&gt;” task, which enables logging. Depending on what file you’re starting from, this might be done already – but what you need is to ensure that task is added to the “&lt;code&gt;/Register/Tasks&lt;/code&gt;” block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Register&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SetVariable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Set-Variable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WriteInformation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write-Information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that done, you can now add your own tasks to output whatever generated values you’re after. So after the generation block in “&lt;code&gt;/Tasks&lt;/code&gt;” above, you might add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="cm"&gt;/* Whatever generation tasks you need */&lt;/span&gt;

        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DisplaySqlCorePassword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Displays the Sql Core DB password.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WriteInformation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Params&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MessageData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[concat('Sql Core DB Password: ',variable('SqlCore.Password'))]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;InformationAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Continue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in order to get the password generated for the Core database. And you can add more of these to write out whatever passwords you need to reuse:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/04/sifloggeddata.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F04%2Fsifloggeddata.png%3Fw%3D800%26h%3D682"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s just binding a task based on “&lt;code&gt;WriteInformation&lt;/code&gt;” which outputs the string defined by “&lt;code&gt;MessageData&lt;/code&gt;“. So you can construct more complex messages if you want, too.&lt;/p&gt;

&lt;p&gt;Just remember not to leave log files lying around with these values in them…&lt;/p&gt;

</description>
      <category>installation</category>
      <category>sif</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>A little PowerShell hack for sending files to a remote machine</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 10 Jun 2019 08:03:59 +0000</pubDate>
      <link>https://dev.to/jermdavis/a-little-powershell-hack-for-sending-files-to-a-remote-machine-21o4</link>
      <guid>https://dev.to/jermdavis/a-little-powershell-hack-for-sending-files-to-a-remote-machine-21o4</guid>
      <description>&lt;p&gt;I was asked to do some configuration on a remote computer recently, and discovered that the security-concious network admins had locked down the ability for me to share my local computer’s files with the server via RDP and the ability to get to services like OneDrive. I had a collection of config files I had been asked to deploy, and manually creating each file on the server and copying over its contents seemed like a lot of hassle. So I tried a trick with PowerShell to make my life easier…&lt;/p&gt;

&lt;p&gt;[&lt;strong&gt;&lt;em&gt;Please note:&lt;/em&gt;&lt;/strong&gt; I’m not condoning breaking network security policies here – don’t use this in a way that would get you into trouble!]&lt;/p&gt;

&lt;p&gt;Ages ago I wrote a post about how you can &lt;a href="https://jermdavis.wordpress.com/2017/12/24/embedding-resources-in-powershell-scripts/"&gt;embed files directly into PowerShell scripts&lt;/a&gt; using Base64 encoding. Since that idea can turn an arbitrary file into text that you can save in a script, it can also be used to encode an arbitrary file and place it onto the clipboard. And that gives you a route to move the file over an RDP session’s shared clipboard – which looked like it might be an approach to making my situation easier. So I hacked up a little PowerShell module that can be used to achieve this…&lt;/p&gt;

&lt;p&gt;So on one machine we’re going to need to send a specific file to the clipboard. That entails a commandlet that takes the path to a file, checks it exists and sends the file to the clipboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Send-ClipboardFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Specified file '&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;' cannot be found"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$fileName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Split-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-leaf&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Base64Encode-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fileName&lt;/span&gt;&lt;span class="se"&gt;`b&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Clipboard&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File &lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="s2"&gt; encoded to clipboard in &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; bytes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having checked the file exists, the code uses &lt;code&gt;Split-Path&lt;/code&gt; to make sure it has just the file name, ignoring any relative path that may have been passed in. It then turns the file into a base64 string. Finally it sends a string to the clipboard that combines the file name and the encoded data and writes some output.&lt;/p&gt;

&lt;p&gt;Encoding the file to a base64 string is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Base64Encode-File&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;process&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Byte&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Convert&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;ToBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All it needs to do is read the contents of the file, and call the .Net function to convert the value into base64.&lt;/p&gt;

&lt;p&gt;The other end of the process involves receiving the data from the clipboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Receive-ClipboardFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$alternativeName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$clip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Clipboard&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$bits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$clip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Data error. Expected 2 items of data on clipboard, but got &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$bits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$alternativeName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$alternativeName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$alternativeName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Receiving file &lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="s2"&gt; from &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$clip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; bytes of data"&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Write-EmbeddedFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bit of code grabs the data that’s on the clipboard, and tries to split it using the separator character that was used in the sending routine above. It checks to see that it got exactly two items from that. If not, that implies an error. Otherwise you have a filename and a block of encoded data. If the user passed the optional alternative file name parameter then it uses that, otherwise it uses the original file name sent over the clipboard. Then it can write some output, and then turn the clipboard data back into a disk file.&lt;/p&gt;

&lt;p&gt;And again, writing the data out to disk is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Write-EmbeddedFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$targetFile&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;process&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Convert&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;FromBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$base64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Set-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\&lt;/span&gt;&lt;span class="nv"&gt;$targetFile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Byte&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the module file can export the two key functions for use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Export-ModuleMember&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Receive-ClipboardFile"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Export-ModuleMember&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Send-ClipboardFile"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, all you have to do is drop this script (I named it “&lt;code&gt;ClipboardFileTransfer.psm1&lt;/code&gt;“) into a suitably named folder in your modules directory or locally and you can import it and send some files…&lt;/p&gt;

&lt;p&gt;For example, if you have the module in a local directory on the local machine you might run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/05/sendafile.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Lihxw2K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/05/sendafile.png%3Fw%3D800%26h%3D316" alt="" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And on the remote one you can run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/05/receiveafile.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z5eToV0Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/05/receiveafile.png%3Fw%3D800%26h%3D316" alt="" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And magically the file is transferred… (And of course you can do it in the opposite direction too)&lt;/p&gt;

&lt;p&gt;[Note to self: tweak script so that the bytes reported at both ends measure the same thing… &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4H1PAnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" width="72" height="72"&gt;&lt;/a&gt; ]&lt;/p&gt;

&lt;p&gt;You need to get this one script onto both of your machines, but if you can do that, this allows for easy transfers of other files. And it made my day a lot simpler.&lt;/p&gt;

&lt;p&gt;If you think this might be useful, &lt;a href="https://gist.github.com/jermdavis/aa3d1cda2875167ef59feaceae6b0961"&gt;the script is available as a gist&lt;/a&gt; for you to use.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>windows</category>
    </item>
    <item>
      <title>A pain point with “Trusted Connection” in Sitecore v9.1</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 27 May 2019 07:26:02 +0000</pubDate>
      <link>https://dev.to/jermdavis/a-pain-point-with-trusted-connection-in-sitecore-v9-1-272k</link>
      <guid>https://dev.to/jermdavis/a-pain-point-with-trusted-connection-in-sitecore-v9-1-272k</guid>
      <description>&lt;p&gt;One of the projects I’m working on at the moment came with a requirement to change Sitecore v9.1 from running with the default SQL Security accounts to trusted connections using specific Active Directory accounts that the client provided. While there’s a bit of work to do to enable this, it shouldn’t be too tough. But trying to be a bit clever, I hit upon an issue which seemed worth documenting…&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of background:
&lt;/h2&gt;

&lt;p&gt;You may well never have delved into the SQL Databases that Sitecore provisions, but some of the more modern ones include a stored procedure called &lt;code&gt;GrantLeastPrivilege&lt;/code&gt;. They don’t seem to be officially documented, but when you examine them, they’re helper scripts that grant exactly the database rights that a user account needs to be able to access a specific Sitecore database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/05/grantleastprivscrpt.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1PaAQaKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/05/grantleastprivscrpt.png%3Fw%3D800%26h%3D485" alt="" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you’d hope that they’d be helpful if you need to move from one set of database accounts to another…&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue:
&lt;/h2&gt;

&lt;p&gt;But when I tried to use these scripts with the Active Directory accounts provided by the client, I got oddly inconsistent results. When I tried to call the procedure in some databases with my user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MyInstance&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_Xdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shard0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xdb&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_collection&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;GrantLeastPrivilege&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="s1"&gt;'Domain&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s1"&gt;rocessAccountName'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I got success. It would write out all the changes it made. But when I called the same procedure in other databases, with the same approach&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MyInstance&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_ProcessingEngineStorage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sitecore&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_processing&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;GrantLeastPrivilege&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="s1"&gt;'Domain&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s1"&gt;rocessAccountName'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I got error messages instead – saying something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Msg 102, Level 15, State 1, Line 9Incorrect syntax near '\'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I tested it, across all the non-content databases, it broke on ProcessingEngineStorage, MarketingAutomation, Processing.Pools, Processing.Tasks, ProcessingEngineTasks and ReferenceData.&lt;/p&gt;

&lt;p&gt;Looking at the underlying code, the scripts which work run statements in the form of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'grant execute on TYPE::[database].[thing] TO ['&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the ones that fail look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'grant execute on TYPE::[database].[thing] TO '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so it’s pretty easy to tweak them to be successful. But it’s a bit of a pain that this doesn’t work out of the box. So I’ve reported this to Sitecore Support, who have accepted it’s a bug. If you’re hitting this issue too, make reference to bug 331327.&lt;/p&gt;

&lt;p&gt;(And yes, I’m aware of the whole “string concatenation is dangerous when generating SQL statements thing – that’s a debate for another day)&lt;/p&gt;

</description>
      <category>bug</category>
      <category>installation</category>
      <category>sitecore</category>
    </item>
    <item>
      <title>Dealing with .tar.gz files on Windows Server</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 13 May 2019 07:17:55 +0000</pubDate>
      <link>https://dev.to/jermdavis/dealing-with-tar-gz-files-on-windows-server-2ikk</link>
      <guid>https://dev.to/jermdavis/dealing-with-tar-gz-files-on-windows-server-2ikk</guid>
      <description>&lt;p&gt;A couple of times recently, I’ve found myself needing to deploy files that come wrapped in a &lt;code&gt;.tar.gz&lt;/code&gt; archive onto servers. On your desktop that’s not too much of a problem – you just run the installer for your preferred &lt;a href="https://www.7-zip.org/" rel="noopener noreferrer"&gt;3rd party tool&lt;/a&gt;, or maybe &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10" rel="noopener noreferrer"&gt;use the new Unixy shell&lt;/a&gt; and you get on with it. But on client servers security can be higher and you don’t always get the option to run any old installer. So I needed an alternative…&lt;/p&gt;

&lt;p&gt;Looking for inspiration, I did a bit of googling and came across &lt;a href="https://stackoverflow.com/questions/38776137/native-tar-extraction-in-powershell" rel="noopener noreferrer"&gt;a thread on Stack Overflow&lt;/a&gt; which suggested there is a &lt;a href="https://github.com/thoemmi/7Zip4Powershell" rel="noopener noreferrer"&gt;PowerShell extension for handling &lt;code&gt;.tar&lt;/code&gt; files&lt;/a&gt;. It’s based on 7Zip’s libraries – but it doesn’t require installing the full 7Zip toolset, and it can be fetched direct from the PowerShell module feed.&lt;/p&gt;

&lt;p&gt;That seemed like a good staring point – but the code in the answers was going to need a bit of work. So I’ve taken that as a basis and produced my own script to use to deal with &lt;code&gt;.tar.gz&lt;/code&gt; archives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main script
&lt;/h2&gt;

&lt;p&gt;To be useful, the script is going to need to receive a file to extract, and a folder to put the results into. That’s easily done by declaring a couple of mandatory parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cmdletbinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;SupportsShouldProcess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$True&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$True&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$True&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$TargetFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$BufferSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters also declare that this script will honour the “&lt;code&gt;-whatif&lt;/code&gt;” parameter via the &lt;code&gt;cmdletbinding()&lt;/code&gt; attribute at the top. And it declares an optional parameter for the size of the file IO buffer used when extracting the &lt;code&gt;.gzip&lt;/code&gt; stream – more on that later.&lt;/p&gt;

&lt;p&gt;The logic of the script is fairly simple. First it needs to do some basic validation of the file it’s going to process. First it can test the file actually exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Source file '&lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="s2"&gt;' does not exist"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then it can test that it has the right extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".tar.gz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CurrentCultureIgnoreCase"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Source file '&lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="s2"&gt;' does not have a .tar.gz extension"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it’s happy, the overall processing is broken up into six operations. The first two make sure that the source file has an absolute path, before working out the right name for the &lt;code&gt;.tar&lt;/code&gt; file that will be hiding inside the &lt;code&gt;.tar.gz&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Once that’s done, the real work is to expand the GZip data to get the &lt;code&gt;.tar&lt;/code&gt; file. That can be done in native .Net code that I’ll get to in a sec. Then it has to make sure the extension for handling &lt;code&gt;.tar&lt;/code&gt; files is installed – by grabbing a copy of the “7Zip4PowerShell” PowerShell module if it’s not available already. That can then be called to extract the data, before finally deleting the temporary &lt;code&gt;.tar&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Resolve-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Calculate-TarFileName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Expand-GZip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FileToExtract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$BufferSize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Ensure-7Zip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Extract-Tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TargetFolder&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSCmdlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShouldProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Remove temporary tar file'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s in all those functions? Read on…&lt;/p&gt;

&lt;h2&gt;
  
  
  Expanding the GZip stuff
&lt;/h2&gt;

&lt;p&gt;The first step is working out the name for the output from the GZip file – which is the tar file. That’s pretty trivial, as it just means stripping the final “&lt;code&gt;.gz&lt;/code&gt;” off the input filename:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Calculate-TarFileName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$targzFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$targzFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$targzFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastIndexOfAny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expanding the GZip file takes a bit more work. This is largely &lt;a href="https://stackoverflow.com/a/42165686/847953" rel="noopener noreferrer"&gt;cribbed from one of the answers on the Stack Overflow thread&lt;/a&gt; referenced above – bit with some enhancements. Firstly it now understands &lt;code&gt;-whatif&lt;/code&gt; and won’t actually generate the new file in that scenario. And secondly it adds some code to enable a progress bar. Other than that, it’s basically just processing the &lt;code&gt;GzipStream&lt;/code&gt; using standard .Net code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Expand-GZip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cmdletbinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;SupportsShouldProcess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$True&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$infile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$outFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$bufferSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$fileSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Original-GzipFileSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$inFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSCmdlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShouldProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$infile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Expand gzip stream"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.IO.FileStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$inFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileMode&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileAccess&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileShare&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.IO.FileStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$outFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileMode&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileAccess&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileShare&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$gzipStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.IO.Compression.GzipStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.Compression.CompressionMode&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Decompress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="nv"&gt;$buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="nv"&gt;$bufferSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="nv"&gt;$pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fileSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extracting tar from gzip"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PercentComplete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pc&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="nv"&gt;$read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$gzipstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bufferSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$read&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-le&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extracting tar from gzip"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Completed&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="kr"&gt;break&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="nv"&gt;$gzipStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making a useful progress bar involves knowing the size of the final stream, however. But Google to the rescue again here, as it pointed me towards &lt;a href="https://www.codeproject.com/Tips/638039/GZipStream-length-when-uncompressed" rel="noopener noreferrer"&gt;this CodeProject posting that describes the C# code to achieve this&lt;/a&gt;. Turns out you just need to verify that it’s actually a GZip stream (looking at the first three bytes) and the find the last four bytes to get an &lt;code&gt;Int32&lt;/code&gt; that is the original length:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Original-GzipFileSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$gzipFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$fs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.IO.FileStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$gzipFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileMode&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileAccess&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;IO.FileShare&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c"&gt;# If magic numbers are 31 and 139 and the deflation id is 8 then this is a file to process&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;139&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$ba&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.IO.SeekOrigin&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nx"&gt;End&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;System.BitConverter&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;ToInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File '&lt;/span&gt;&lt;span class="nv"&gt;$gzipFile&lt;/span&gt;&lt;span class="s2"&gt;' does not have the correct gzip header"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;finally&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the &lt;code&gt;Expand-GZip&lt;/code&gt; function can use that to work out a percentage completion as it iterates through blocks of the stream…&lt;/p&gt;

&lt;p&gt;I mentioned before that the overall script has an option for block sizes for processing here. It defaults to 1KB here because that’s what was in the code I cribbed, but you can pass a bigger block size to trade off speed for memory usage.&lt;/p&gt;

&lt;p&gt;But once that’s complete the initial &lt;code&gt;.tar.gz&lt;/code&gt; file will have a &lt;code&gt;.tar&lt;/code&gt; alongside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dealing with the .tar file
&lt;/h2&gt;

&lt;p&gt;The original &lt;a href="https://stackoverflow.com/a/46876070/847953" rel="noopener noreferrer"&gt;Stack Overflow thread included an answer that suggested the “7Zip4PowerShell” module&lt;/a&gt; for PowerShell would be the simplest approach here. This code breaks it up into two tasks – one to make sure the module is available locally to use, and the other to actually use it.&lt;/p&gt;

&lt;p&gt;The thread talks about two approches to using that module. One where you manually copy the required files locally and the script picks those up and uses them, and one where it asks the &lt;code&gt;Install-Package&lt;/code&gt; commandlet to fetch it from the public feed. For laughs I decided to combine the two, as I could see scenarios where both mught be useful.&lt;/p&gt;

&lt;p&gt;So if you put the files for the package to the “7Zip4Powershell” folder next to the script, it’ll spot them and use this local copy. To get those files you can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Save-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;7Zip4Powershell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;from a prompt. This approach will be most useful on highly-secured servers where the admins want to vet all the files your work uses before you do anything with them. You can hand over both this script and the files for that module for investigation, and they just need copying to the server for use.&lt;/p&gt;

&lt;p&gt;Alternatively, if that folder does not exist, it’ll pull the module from the public feed. That’s the “zero effort” approach when you’re allowed to use it. You don’t need to do any extra work – it’ll just pull in the right code if it’s not already installed for you. And as before, the logic is wrapped up to support &lt;code&gt;-WhatIf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Ensure-7Zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$pathToModule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\7Zip4Powershell\1.9.0\7Zip4PowerShell.psd1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Expand-7Zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ignore&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pathToModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSCmdlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShouldProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pathToModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Install 7Zip module from local path"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Activity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installing the 7Zip4PowerShell module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Using local module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PercentComplete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Import-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pathToModule&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Activity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installing the 7Zip4PowerShell module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Using local module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Completed&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSCmdlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShouldProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PowerShell feed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Install 7Zip module'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;-Activity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installing the 7Zip4PowerShell module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Using public feed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PercentComplete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nv"&gt;$progressPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'silentlyContinue'&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Install-Package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CurrentUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;7Zip4PowerShell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nv"&gt;$progressPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Continue'&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;-Activity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installing the 7Zip4PowerShell module"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Using public feed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Completed&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the moment this code is hard-coded to the current version of this module – Making that more flexible is on my backlog, if I get a chance…&lt;/p&gt;

&lt;p&gt;Once the code installed, the actual command to run the extraction is easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Extract-Tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cmdletbinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;SupportsShouldProcess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$True&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSCmdlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShouldProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Expand tar file"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Expand-7Zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tarFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that adds on top of the 7Zip command is the “&lt;code&gt;-WhatIf&lt;/code&gt;” logic, as that doesn’t seem to be supported by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  In action…
&lt;/h2&gt;

&lt;p&gt;So with that installed, you can extract files to your hearts content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/05/extracttargz.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F05%2Fextracttargz.png%3Fw%3D800%26h%3D356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you pass &lt;code&gt;-WhatIf&lt;/code&gt; then it tells you what it would do, but does nothing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/05/extracttargzwhatif.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjermdavis.files.wordpress.com%2F2019%2F05%2Fextracttargzwhatif.png%3Fw%3D800%26h%3D327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve &lt;a href="https://gist.github.com/jermdavis/65c8b8f45397ec35b8a8ceca67510e96" rel="noopener noreferrer"&gt;put the full code up in a gist&lt;/a&gt; if you think it would be useful for you…&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>windows</category>
    </item>
    <item>
      <title>Pasting multiple cells into Excel from PowerShell</title>
      <dc:creator>Jeremy Davis</dc:creator>
      <pubDate>Mon, 15 Apr 2019 07:31:34 +0000</pubDate>
      <link>https://dev.to/jermdavis/pasting-multiple-cells-into-excel-from-powershell-3hba</link>
      <guid>https://dev.to/jermdavis/pasting-multiple-cells-into-excel-from-powershell-3hba</guid>
      <description>&lt;p&gt;Sometimes the learning point from working on a misbehaving Sitecore server isn’t related to the CMS. Recently I learned something useful about Excel while I was addressing some other issues. Not sure if this is “so trivially simple I’m just the last one to realise” or whether it’s a really useful bit of trivia – but just because someone else might benefit:&lt;/p&gt;

&lt;p&gt;I was monitoring an issue with a Sitecore server recently, and to keep track of what had been going on, I’d knocked up a graph in Excel. Every so often I was looking at a certain data value on the server, and then copy/pasting that into the spreadsheet along with the date/time of when the measurement was taken.&lt;/p&gt;

&lt;p&gt;Having done that a few times I was struck by the urge to make my life simpler. For… &lt;a href="https://media.giphy.com/media/9rAqbQNmPnQm9BRwsw/giphy.gif"&gt;reasons&lt;/a&gt;… it wasn’t practical to have PowerShell writing out a CSV file to import into Excel in one go – so could I get PowerShell to give me the date and the value on my clipboard, so I’d just have to paste it into my spreadsheet?&lt;/p&gt;

&lt;p&gt;Yes of course:&lt;/p&gt;

&lt;p&gt;First up, getting the current date/time is handled by the “&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date?view=powershell-6"&gt;&lt;code&gt;Get-Date&lt;/code&gt;&lt;/a&gt;” commandlet. It happened that the server I was using was configured for a US locale, and being a Brit that meant I wanted my date formatted differently to the default output. Not a problem – you just need to tell PowerShell how you’d like your date using standard .Net format string data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$timeStamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dd/MM/yyyy hh:mm"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grabbing the actual measurement isn’t relevant here, but I squirrelled that data away in a variable too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;/\&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;someMagicToMeasureAValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;\&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can send stuff to the clipboard automatically with the “&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-clipboard?view=powershell-5.1"&gt;&lt;code&gt;Set-Clipboard&lt;/code&gt;&lt;/a&gt;” commandlet.&lt;/p&gt;

&lt;p&gt;But my spreadsheet was formatted with a column for the timestamp and a column for the value, so I could plot a graph from the data. So how do you get the data onto the clipboard in the right format so that pasting into Excel will make sure it goes into two cells?&lt;/p&gt;

&lt;p&gt;Well after some fun with Google, it turns out this is kind of obvious in retrospect: You just tab-separate your data. In PowerShell “`t” (back-tick, followed by “t”) is a tab character. And that means I can send my two columns of data to the clipboard with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;`t&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Clipboard&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if I hit paste into Excel, I get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jermdavis.files.wordpress.com/2019/03/pastetwocells.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EGv8jp7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jermdavis.files.wordpress.com/2019/03/pastetwocells.png%3Fw%3D800%26h%3D545" alt="" width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can put multiple tabs in if you want to paste more than one column…&lt;/p&gt;

</description>
      <category>general</category>
      <category>powershell</category>
      <category>excel</category>
    </item>
  </channel>
</rss>
