<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://blog.teknikcs.ca/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.teknikcs.ca/" rel="alternate" type="text/html" /><updated>2023-08-11T01:34:48+00:00</updated><id>https://blog.teknikcs.ca/feed.xml</id><title type="html">TeknikCS Blog</title><subtitle>Error Arcana is a site devoted to weird one-off bugs, errors, and undocumented quirks. We cover a wide variety of issues mostly on windows software but also some embedded and firmware weirdness.</subtitle><author><name>James Foley</name></author><entry><title type="html">Converting Between Float and Fixed-Point with an ESP32</title><link href="https://blog.teknikcs.ca/2023/08/10/esp32-float-fixed-point-conversions" rel="alternate" type="text/html" title="Converting Between Float and Fixed-Point with an ESP32" /><published>2023-08-10T00:00:00+00:00</published><updated>2023-08-10T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2023/08/10/esp32-float-fixed-point-conversions</id><content type="html" xml:base="https://blog.teknikcs.ca/2023/08/10/esp32-float-fixed-point-conversions"><![CDATA[<p class="notice--tlwr">TL;WR: You can use <code class="language-plaintext highlighter-rouge">FLOOR.S</code>, <code class="language-plaintext highlighter-rouge">CIEL.S</code>, <code class="language-plaintext highlighter-rouge">ROUND.S</code>, <code class="language-plaintext highlighter-rouge">FLOAT.S</code>, and <code class="language-plaintext highlighter-rouge">UFLOAT.S</code> in a single inline-assembly instruction to convert between fixed-point and float values.<br />Example implementations at bottom of page.</p>

<p class="notice--warning">There are alternate double versions of the instructions in the xtensa ISA <strong>but</strong> they are not implemented in the ESP32 tensilica cores.</p>

<p class="notice--success">You can find the full xtensa assembly instruction set documentation on the Cadence website <a href="https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf">https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf</a>.</p>

<p>If you’re writing code that uses floating point numbers you’re already using some of these instructions. The toolchain will use them whenever you convert between <code class="language-plaintext highlighter-rouge">int</code>/<code class="language-plaintext highlighter-rouge">uint</code> and <code class="language-plaintext highlighter-rouge">float</code> types.</p>

<p>The instructions have a third parameter which specifies how many bits come after the decimal point (fractional bits). A value of <code class="language-plaintext highlighter-rouge">1</code> would be ±0.5 in the last bit, <code class="language-plaintext highlighter-rouge">2</code> would be ±0.25 in the last two bits, etc.</p>

<p>The normal assembly emitted when doing something like <code class="language-c++ highlighter-rouge highlight highlighter-rouge"><span class="kt">float</span> <span class="n">f</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="mi">53</span></code> will have it set to 0 (no fractional bits), but by calling the instruction manually you can specify the precision.</p>

<p>The fractional bits parameter is a 0..15 constant and cannot be specified at runtime, so if you need different shapes you would just implement as separate methods, templates, or overloading methods with custom types.</p>

<p>For example, the instruction <code class="language-plaintext highlighter-rouge">FLOAT.S f0, a2, 0</code> with <code class="language-plaintext highlighter-rouge">53</code> in register <code class="language-plaintext highlighter-rouge">a2</code> would convert <code class="language-plaintext highlighter-rouge">53</code> to <code class="language-plaintext highlighter-rouge">53.0f</code> in the <code class="language-plaintext highlighter-rouge">f0</code> register. However the instruction <code class="language-plaintext highlighter-rouge">FLOAT.S f0, a2, 1</code> would convert it to <code class="language-plaintext highlighter-rouge">26.5f</code>.</p>

<figure class="">
<table>
  <thead>
    <tr>
      <th>Fractional Bits</th>
      <th>Example Instruction</th>
      <th>Input Binary</th>
      <th>Input Int</th>
      <th>Result Float</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0 (normal float&lt;&gt;int)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 0</code></td>
      <td>0b00110100</td>
      <td>52</td>
      <td>52.0</td>
    </tr>
    <tr>
      <td>0 (normal float&lt;&gt;int)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 0</code></td>
      <td>0b00110101</td>
      <td>53</td>
      <td>53.0</td>
    </tr>
    <tr>
      <td>1 (lsb is ±0.5)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 1</code></td>
      <td>0b00110100</td>
      <td>52</td>
      <td>26.0</td>
    </tr>
    <tr>
      <td>1 (lsb is ±0.5)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 1</code></td>
      <td>0b00110101</td>
      <td>53</td>
      <td>26.5</td>
    </tr>
    <tr>
      <td>2 (lsb is ±0.25)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 2</code></td>
      <td>0b00110100</td>
      <td>52</td>
      <td>13.0</td>
    </tr>
    <tr>
      <td>2 (lsb is ±0.25)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 2</code></td>
      <td>0b00110101</td>
      <td>53</td>
      <td>13.25</td>
    </tr>
    <tr>
      <td>2 (lsb is ±0.25)</td>
      <td><code class="language-plaintext highlighter-rouge">FLOAT.S f*n*, a*n*, 2</code></td>
      <td>0b00110110</td>
      <td>54</td>
      <td>13.50</td>
    </tr>
  </tbody>
</table>

  <figcaption>Fig 1. Example Values</figcaption>
</figure>

<p>The <code class="language-plaintext highlighter-rouge">FLOOR.S</code>, <code class="language-plaintext highlighter-rouge">CIEL.S</code>, and <code class="language-plaintext highlighter-rouge">ROUND.S</code> instructions convert float to fixed-point by the method in their name. Since fixed-point can’t try to store arbitrary real numbers you are telling the processor how you’d like the number to be butchered. The <code class="language-plaintext highlighter-rouge">FLOAT.S</code> and <code class="language-plaintext highlighter-rouge">UFLOAT.S</code> instructions just use the default rounding/inf/NaN settings to convert fixed-point to float.</p>

<figure class="fullwidth align-center">
<iframe width="100%" height="800px" src="https://godbolt.org/e?hideEditorToolbars=true#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:'int+toFixed(float+f)%0A%7B%0A++++int+result%3B%0A++++asm(%22ROUND.S+%250,+%251,+1%5Ct%5Cn%22%0A++++++++:+%22%3Da%22+(result)%0A++++++++:+%22f%22+(f))%3B%0A++++return+result%3B%0A%7D%0A%0Afloat+toFloat(int+i)%0A%7B%0A%09float+result%3B%0A%09asm(%22FLOAT.S+%250,+%251,+1%5Ct%5Cn%22%0A%09%09:+%22%3Df%22+(result)%0A%09%09:+%22a%22+(i))%3B%0A%09return+result%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.879000083036445,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:esp32g20230208,deviceViewOpen:'1',filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'1',libraryCode:'1',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O1',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+Xtensa+ESP32+gcc+12.2.0+(20230208)+(Editor+%231)',t:'0')),k:49.120999916963555,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"></iframe>

  <figcaption>Fig 2. Simple Implementation</figcaption>
</figure>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[Fixed point math for dsp or pga's can be annoying to write in C. Fortunately there's an assembly instruction.]]></summary></entry><entry><title type="html">Windows Service Recovery Action Blob Manglement!</title><link href="https://blog.teknikcs.ca/2023/02/27/windows-service-recovery-action-blob-modification" rel="alternate" type="text/html" title="Windows Service Recovery Action Blob Manglement!" /><published>2023-02-27T00:00:00+00:00</published><updated>2023-02-27T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2023/02/27/windows-service-recovery-action-blob-modification</id><content type="html" xml:base="https://blog.teknikcs.ca/2023/02/27/windows-service-recovery-action-blob-modification"><![CDATA[<p class="notice--tlwr">TL;WR: You can make chaotic-neutral customizations to Windows service recovery actions!<br />(ᴘʟᴇᴀsᴇ ᴅᴏɴ’ᴛ ᴅᴏ ᴛʜɪs)</p>

<p class="notice--info">Shameless plug: I’m going to be using <a href="https://github.com/WerWolv/ImHex">ImHex</a> in this post and. It has an eye-watering featureset and a bunch of existing patterns <a href="https://github.com/WerWolv/ImHex-Patterns/tree/master/patterns">WerWolv/ImHex-Patterns</a>. Definitely recommend that you check it out for binary data analysis.</p>

<p>You can actually edit the recovery actions of a service to include more steps that the first, second, and subsequent options available in the UI. Before I explain I just want to get the following points out of the way:</p>
<ol>
  <li>Don’t do this. It’s a bad idea. No support whatsoever and nobody else will ever think to check for it. This whole post is a <a href="https://en.wikipedia.org/wiki/Concertina_wire">concertina wire</a> bundle of edge cases.</li>
  <li>If you actually need customizable recovery you should do <em>anything</em> else. Alternatives include: watchdog services, wrapper/manager services, scheduled tasks, recovery scripts, hand cranked flashlights, sound powered telephones, or spark-gap radio.</li>
</ol>

<p>With that out of the way, service recovery actions as described in the <code class="language-plaintext highlighter-rouge">services.msc</code> settings are stored as a binary blob in the registry. You’ll find the blob at <code class="language-plaintext highlighter-rouge">HKLM:\\SYSTEM\CurrentControlSet\Services\[SERVICENAME]</code> under the <code class="language-plaintext highlighter-rouge">FailureActions</code> binary key. I’m going to generate an example blob that does the following so that we can review it in more detail.</p>
<ol>
  <li>Restart the Services</li>
  <li>Run a Program (cmd.exe)</li>
  <li>Take no Action</li>
</ol>

<figure class="threequartersize align-center">
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F
00000000  00 00 00 00 00 00 00 00  01 00 00 00 03 00 00 00
00000010  14 00 00 00 01 00 00 00  60 EA 00 00 03 00 00 00
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
</code></pre></div></div>

  <figcaption>Fig 1. Example Blob</figcaption>
</figure>

<p>Fortunately for us this isn’t as opaque as it seems. It’s a pretty simple instance of <a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/58032b71-1e5c-4f2e-8545-34b0f2e8c6ad">SERVICE_FAILURE_ACTIONS</a> with an array of <a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/5e3e9ca1-6c33-4c94-bf36-c5b75262d7d6">SC_ACTION</a>. We can use the following ImHex pattern code to parse this for us:</p>

<figure class="threequartersize align-center">
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="n">SC_ACTION_TYPE</span><span class="o">:</span> <span class="n">u32</span> <span class="p">{</span>
	<span class="n">NONE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
	<span class="n">REBOOT</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
	<span class="n">RESTART</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
	<span class="n">RUN_CMD</span> <span class="o">=</span> <span class="mi">3</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="nc">SC_ACTION</span> <span class="p">{</span>
	<span class="n">SC_ACTION_TYPE</span> <span class="n">Type</span><span class="p">;</span>
	<span class="n">u32</span> <span class="n">Delay</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="nc">SERVICE_FAILURE_ACTIONS</span> <span class="p">{</span>
    <span class="n">u32</span>     <span class="n">dwResetPeriod</span><span class="p">;</span>
    <span class="n">u32</span>    <span class="n">lpRebootMsg</span><span class="p">;</span>
    <span class="n">u32</span>    <span class="n">lpCommand</span><span class="p">;</span>
    <span class="n">u32</span>     <span class="n">cActions</span><span class="p">;</span>
    <span class="n">SC_ACTION</span> <span class="o">*</span><span class="n">lpsaActions</span><span class="p">[</span><span class="n">cActions</span><span class="p">]</span> <span class="o">:</span> <span class="n">s32</span><span class="p">;</span>
<span class="p">};</span>

<span class="n">SERVICE_FAILURE_ACTIONS</span> <span class="n">actions</span> <span class="err">@</span> <span class="mh">0x00</span><span class="p">;</span>
</code></pre></div></div>
  <figcaption>Fig 2. ImHex Pattern for SERVICE_FAILURE_ACTIONS</figcaption>
</figure>

<p>Now we pop that blob and the pattern into ImHex and we get a nice, intuitive breakdown of the binary:</p>

<figure class="">
  <img src="/assets/images/windows-service-recovery-imhex.png" alt="A screenshot above blob in ImHex." /><figcaption>
      Fig 3. ImHex Breakdown

    </figcaption></figure>

<p>With that you should have an idea where we’re going with this. You can change the lpsaActions array to as many as 1024 entries which I find hilarious. All you need to do is change the cActions value and add your new actions. We’re going to change it to 4 entries.</p>

<figure class="align-center">
<table>
  <thead>
    <tr>
      <th>Var</th>
      <th>Offset</th>
      <th>Hex</th>
      <th>Value</th>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>dwResetPeriod</td>
      <td>0x00 : 0x03</td>
      <td>0x00000000</td>
      <td>0 Seconds</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpRebootMsg</td>
      <td>0x04 : 0x07</td>
      <td>0x00000000</td>
      <td>N/A (no pointer to a reboot message string)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpCommand</td>
      <td>0x08 : 0x0B</td>
      <td>0x00000001</td>
      <td>cmd.exe (value of 1 means load command from other registry keys)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>cActions</td>
      <td>0x0C : 0x0F</td>
      <td>0x00000004</td>
      <td>Array size of 4 Actions</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>*lpsaActions</td>
      <td>0x10 : 0x13</td>
      <td>0x00000014</td>
      <td>Pointer to the array (next DWORD)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[0]:Type</td>
      <td>0x14 : 0x17</td>
      <td>0x00000001</td>
      <td>SC_ACTION_TYPE RESTART (restart service)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[0]:Delay</td>
      <td>0x18 : 0x1B</td>
      <td>0x0000EA60</td>
      <td>Delay 60000 ms</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[1]:Type</td>
      <td>0x1C : 0x1F</td>
      <td>0x00000001</td>
      <td>SC_ACTION_TYPE RESTART (restart service)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[1]:Delay</td>
      <td>0x20 : 0x23</td>
      <td>0x0000EA60</td>
      <td>Delay 60000 ms</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[2]:Type</td>
      <td>0x24 : 0x27</td>
      <td>0x00000001</td>
      <td>SC_ACTION_TYPE RESTART (restart service)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[2]:Delay</td>
      <td>0x28 : 0x2B</td>
      <td>0x0000EA60</td>
      <td>Delay 60000 ms</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[3]:Type</td>
      <td>0x2C : 0x2F</td>
      <td>0x00000003</td>
      <td>SC_ACTION_TYPE RUN_CMD (run a command)</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>lpsaAction[3]:Delay</td>
      <td>0x30 : 0x33</td>
      <td>0x0000EA60</td>
      <td>Delay N/A</td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

  <figcaption>Fig 4. New blob breakdown</figcaption>
</figure>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[Ever wanted more than three recovery actions? Here's a sketchy hack that you shouldn't do!]]></summary></entry><entry><title type="html">FYI: O365 Exchange IMAP Search is a hot mess</title><link href="https://blog.teknikcs.ca/2023/02/26/fyi-o365-imap-search-is-a-hot-mess" rel="alternate" type="text/html" title="FYI: O365 Exchange IMAP Search is a hot mess" /><published>2023-02-26T00:00:00+00:00</published><updated>2023-02-26T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2023/02/26/fyi-o365-imap-search-is-a-hot-mess</id><content type="html" xml:base="https://blog.teknikcs.ca/2023/02/26/fyi-o365-imap-search-is-a-hot-mess"><![CDATA[<p class="notice--tlwr">TL;WR: Exchange IMAP Searches are kind of broken. Exchange IMAP doesn’t support searching for categories, but you can work around that by searching for your category tag in the body. Make sure you only use lowercase letters in your search.</p>

<p>I was setting up <a href="https://docs.paperless-ngx.com/">paperless-ngx</a> as an experiment for personal document management and wanted to be able to ingest emails &amp; attachments from an O365 account. I wanted to be able to be able to tell Paperless to ingest an email by setting a category flag (e.g. Paperless-Inbox) so that I could archive emails without moving them from their folders. Unfortunately Exchange On-Prem and Online do not support the KEYWORDS search (<a href="https://learn.microsoft.com/en-us/openspecs/exchange_standards/ms-stanximap/8a9ff967-52be-4efa-bf48-29e0036748cc">See Exchange IMAP Standards V0039</a>).</p>

<p class="notice--info">Note: The email library used by paperless-ngx does not support Modern Authentication, so I built a container to host the headless version of <a href="https://github.com/simonrob/email-oauth2-proxy">email-oauth2-proxy</a></p>

<p>While looking for workarounds I found that Exchange <em>does</em> provide the email categories in a <code class="language-plaintext highlighter-rouge">Keywords:</code> header; however, despite Exchange including <code class="language-plaintext highlighter-rouge">Keywords: My-Category</code> in the <code class="language-plaintext highlighter-rouge">FETCH n (BODY[HEADERS])</code> response, a search using <code class="language-plaintext highlighter-rouge">SEARCH HEADER "Keywords" ""</code> returns no results. Sad trombone.</p>

<p>So if you want to find your categories in an IMAP search your categories must not have spaces and you must use a TEXT search.</p>

<h4 id="undocumented-exchange-imap-rules">Undocumented Exchange IMAP rules</h4>
<ol>
  <li>Searched phrases must be lowercase, uppercase letters in the search will always return no results.</li>
  <li>No spaces are permitted in a search phrase (i.e. <code class="language-plaintext highlighter-rouge">SEARCH BODY "first"</code> works, <code class="language-plaintext highlighter-rouge">SEARCH BODY "second"</code> works, but <code class="language-plaintext highlighter-rouge">SEARCH BODY "first second"</code> gives no results).</li>
  <li>Message category flags <em>do</em> appear in a <code class="language-plaintext highlighter-rouge">TEXT</code> search of a message! This still has the same no-spaces and all-lowercase limitations as a regular BODY search.</li>
  <li>String Literal searches behave the same as quoted string searches.</li>
</ol>

<table style="display: table; margin-left: auto; margin-right: auto;">
  <thead>
    <tr>
      <th>IMAP Command</th>
      <th>Finds Category Flags?</th>
      <th>Find Body Text?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>BODY (Quoted String)</td>
      <td>No</td>
      <td>Yes (lowercase only, no spaces)</td>
    </tr>
    <tr>
      <td>BODY (String Literal)</td>
      <td>No</td>
      <td>Yes (lowercase only, no spaces)</td>
    </tr>
    <tr>
      <td>TEXT (Quoted String)</td>
      <td>Yes</td>
      <td>Yes (lowercase only, no spaces)</td>
    </tr>
    <tr>
      <td>TEXT (String Literal)</td>
      <td>Yes</td>
      <td>Yes (lowercase only, no spaces)</td>
    </tr>
    <tr>
      <td>HEADERS</td>
      <td>No</td>
      <td>No (duh)</td>
    </tr>
  </tbody>
</table>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[The Exchange Online IMAP SEARCH implementation is not well documented]]></summary></entry><entry><title type="html">Can’t Delete a VHD if you Ctrl-C the New-VHD Command</title><link href="https://blog.teknikcs.ca/2022/08/08/cant-delete-vhd-after-cancelling-creation" rel="alternate" type="text/html" title="Can’t Delete a VHD if you Ctrl-C the New-VHD Command" /><published>2022-08-08T00:00:00+00:00</published><updated>2022-08-08T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2022/08/08/cant-delete-vhd-after-cancelling-creation</id><content type="html" xml:base="https://blog.teknikcs.ca/2022/08/08/cant-delete-vhd-after-cancelling-creation"><![CDATA[<p class="notice--tlwr">TL;WR: If you started creating a classic fixed VHD with New-VHD, then tried to cancel it with <kbd>Ctrl</kbd>+<kbd>C</kbd> you’ll probably realize that it’s still stuck initializing in the background. Use WMI to kill it.<br /><a href="#how-to-cancel-a-fixed-vhd-creation-started-by-new-vhd">Click here to skip to the answer</a></p>

<!-- ![image-right](/assets/images/cant-delete-vhd-file-in-use.png){: .align-right} -->
<p>I was experimenting with some data recovery stuff and needed some classic fixed VHD’s. I started creating a 1 TB VHD with <code class="language-plaintext highlighter-rouge">New-VHD</code> but needed to change it so I hit <kbd>Ctrl</kbd>+<kbd>C</kbd> and <em>assumed</em> that would cancel the cmdlet <strong>and</strong> the VHD creation. I assumed incorrectly.</p>

<p>When I tried to delete the VHD I got this error:</p>

<figure class="halfsize align-center">
  <img src="/assets/images/cant-delete-vhd-file-in-use.png" alt="File In Use - The action can't be completed because the file is open in System. Close the file and try again." /><figcaption>
      Fig 1. File In Use, open in System, Ruh Roh!

    </figcaption></figure>

<p>I figured there was still a job creating the VHD so I scanned <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/cim-classes">Virtualization CIM Classes</a> and found the <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/cim-concretejob">CIM_ConcreteJob</a> class, after a quick poke this seemed to be what I was looking for.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select</span><span class="w"> </span><span class="nx">InstanceID</span><span class="p">,</span><span class="w"> </span><span class="nx">Caption</span><span class="p">,</span><span class="w"> </span><span class="nx">Description</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">,</span><span class="w"> </span><span class="nx">Status</span><span class="p">,</span><span class="w"> </span><span class="nx">StatusDescriptions</span><span class="p">,</span><span class="w"> </span><span class="nx">JobStatus</span><span class="p">,</span><span class="w"> </span><span class="nx">PercentComplete</span><span class="p">,</span><span class="w"> </span><span class="nx">Cancellable</span><span class="w">


</span><span class="n">InstanceID</span><span class="w">         </span><span class="p">:</span><span class="w"> </span><span class="s2">"D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"</span><span class="w">
</span><span class="n">Caption</span><span class="w">            </span><span class="p">:</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w"> </span><span class="nx">Creation</span><span class="w">
</span><span class="n">Description</span><span class="w">        </span><span class="p">:</span><span class="w"> </span><span class="nx">Creating</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w">
</span><span class="n">Name</span><span class="w">               </span><span class="p">:</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w"> </span><span class="nx">Creation</span><span class="w">
</span><span class="n">Status</span><span class="w">             </span><span class="p">:</span><span class="w"> </span><span class="nx">OK</span><span class="w">
</span><span class="n">StatusDescriptions</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="n">Job</span><span class="w"> </span><span class="nx">is</span><span class="w"> </span><span class="nx">running</span><span class="p">}</span><span class="w">
</span><span class="n">JobStatus</span><span class="w">          </span><span class="p">:</span><span class="w"> </span><span class="nx">Job</span><span class="w"> </span><span class="nx">is</span><span class="w"> </span><span class="nx">running</span><span class="w">
</span><span class="n">PercentComplete</span><span class="w">    </span><span class="p">:</span><span class="w"> </span><span class="nx">7</span><span class="w">
</span><span class="n">Cancellable</span><span class="w">        </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span></code></pre></div></div>

<p>Now, about killing it: <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/cim-concretejob-requeststatechange">CIM_ConcreteJob.RequestStateChange(RequestedState, TimeoutPeriod)</a>. Looks simple enough!</p>

<p>Alright, lets try to request a safe/clean termination with a 30 second timeout.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$job</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s1">'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'</span><span class="w">
</span><span class="nv">$job</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="err">(</span><span class="nx">Get</span><span class="err">-</span><span class="nx">Date</span><span class="err">).</span><span class="nx">AddSeconds</span><span class="err">(</span><span class="mi">30</span><span class="err">)</span><span class="p">}</span><span class="w">

</span><span class="n">ReturnValue</span><span class="w"> </span><span class="nx">PSComputerName</span><span class="w">
</span><span class="o">-----------</span><span class="w"> </span><span class="o">--------------</span><span class="w">
      </span><span class="mi">32773</span><span class="w">
</span></code></pre></div></div>
<p>Annnd the return code isn’t in the list. On the upside that <em>looks</em> like a common return code (we’ll come back to that in a minute). Let’s try the Kill command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$job</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="err">(</span><span class="nx">Get</span><span class="err">-</span><span class="nx">Date</span><span class="err">).</span><span class="nx">AddSeconds</span><span class="err">(</span><span class="mi">30</span><span class="err">)</span><span class="p">}</span><span class="w">

</span><span class="o">-----------</span><span class="w"> </span><span class="o">--------------</span><span class="w">
      </span><span class="mi">32773</span><span class="w">
</span></code></pre></div></div>
<p>Great, same error. The doc says you can supply a null to indicate there’s no timeout requirement so we’ll try that instead:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$job</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="p">}</span><span class="w">

</span><span class="n">ReturnValue</span><span class="w"> </span><span class="nx">PSComputerName</span><span class="w">
</span><span class="o">-----------</span><span class="w"> </span><span class="o">--------------</span><span class="w">
      </span><span class="mi">32775</span><span class="w">
</span></code></pre></div></div>
<p>Sigh, At least it’s different error. Alright, assumption rechecking time. First, what is the specific Job implementation?</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$job</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Get-Member</span><span class="w"> </span><span class="nt">-View</span><span class="w"> </span><span class="nx">Base</span><span class="w">
</span><span class="c">#TypeName: Microsoft.Management.Infrastructure.CimInstance#root/virtualization/v2/Msvm_StorageJob          </span><span class="w">
</span></code></pre></div></div>
<p>Looks like this is an <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-storagejob">Msvm_StorageJob</a>, let’s see if it’s implementation of <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-storagejob-requeststatechange">RequestStateChange</a> has any juicy details!</p>

<p>The good news is that we have the return codes, the bad news is that they don’t have any descriptions or names. Let’s add a new assumption for a minute and assume that the <a href="https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-storagejob-geterror">GetError</a> method’s return values apply to the RequestStateChange method. Now our errors mean <code class="language-plaintext highlighter-rouge">Invalid Parameter</code> and <code class="language-plaintext highlighter-rouge">Invalid State</code> although that doesn’t get us very far.</p>

<div class="flexme">
<div class="flexhalf">
<h3>Scraped Values</h3>
<table>
    <thead>
        <tr>
            <th>Code</th>
            <th>Description</th>
        </tr>
    </thead>
    <tr>
        <td>Failed</td>
        <td>(32768)</td>
    </tr>
    <tr>
        <td>Access Denied</td>
        <td>(32769)</td>
    </tr>
    <tr>
        <td>Not Supported</td>
        <td>(32770)</td>
    </tr>
    <tr>
        <td>Status is unknown</td>
        <td>(32771)</td>
    </tr>
    <tr>
        <td>Timeout</td>
        <td>(32772)</td>
    </tr>
    <tr>
        <td>Invalid parameter</td>
        <td>(32773)</td>
    </tr>
    <tr>
        <td>System is in used *[sic]*</td>
        <td>(32774)</td>
    </tr>
    <tr>
        <td>Invalid state for this operation</td>
        <td>(32775)</td>
    </tr>
    <tr>
        <td>Incorrect data type</td>
        <td>(32776)</td>
    </tr>
    <tr>
        <td>System is not available</td>
        <td>(32777)</td>
    </tr>
    <tr>
        <td>Out of memory</td>
        <td>(32778)</td>
    </tr>
     <tr>
        <td>Method Parameters Checked - Transition Started</td>
        <td>(4096)</td>
    </tr>
    <tr>
        <td>Invalid State Transition</td>
        <td>(4097)</td>
    </tr>
    <tr>
        <td>Use of Timeout Parameter Not Supported</td>
        <td>(4098)</td>
    </tr>
    <tr>
        <td>Busy</td>
        <td>(4099)</td>
    </tr>
</table>
</div>
<div class="flexhalf">
<h3>Quality Microsoft documentation</h3>
<figure class="">
  <img src="/assets/images/cant-delete-vhd-thanks-microsoft.png" alt="A list of all of the return codes we've been looking at, but they have no titles or descriptions." /><figcaption>
      Fig 2. Thanks Microsoft.

    </figcaption></figure>

</div>
</div>
<p>Alright, maybe it <em>is</em> an invalid parameter. Possibly the .Net DateTime to CIM_DATETIME conversion getting botched? Let’s see if the same thing happens using WMIExplorer.</p>
<figure class="halfsize align-center">
  <img src="/assets/images/cant-delete-vhd-wmi-explorer-nodice.png" alt="WMIExplorer returning the 32773 error code" /><figcaption>
      Fig 3. Weary Sigh.

    </figcaption></figure>

<p>Good News: Calling convention in powershell at least matches the WMI Explorer results. Well, we have another option. The New Disk Wizard <strong>IS</strong> cancellable so let’s see if we can’t reverse engineer how it works. First up we’re going to do a simple WMI trace using event viewer <a href="https://docs.microsoft.com/en-us/windows/win32/wmisdk/tracing-wmi-activity#obtaining-wmi-events-through-event-viewer">Using these instructions</a> and we see multiple events for RequestStateChange on Msvm_StorageJob instances, hmm…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; ProcessId = 57856; Protocol = DCOM; Operation = MI_Session::Invoke; User = NULL; Namespace = root\virtualization\v2
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 117376; Operation = Start IWbemServices::ExecMethod - root\virtualization\v2 : \\.\ROOT\virtualization\v2:Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = 133044712970425349
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 112244; ClassName= Msvm_StorageJob; MethodName = RequestStateChange; ImplementationClass = Msvm_StorageJob; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = \\.\root\virtualization\v2
---
ProviderInfo for GroupOperationId = 117375; Operation = Provider::ExecMethod - VmmsWmiInstanceAndMethodProvider : Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; HostID = 2188; ProviderName = VmmsWmiInstanceAndMethodProvider; ProviderGuid = {0c172fd4-1b2a-11da-994c-0008744f51f3}; Path = 
---
Stop OperationId = 117376; ResultCode = 0x0
</code></pre></div></div>

<p>Unfortunately that didn’t show me the parameters (which makes sense, in WMI method parameters are packed into an object) but it does verify that we’re going about it the right way. To find the parameters we’re going to have to bust out <a href="http://www.rohitab.com/apimonitor">API Monitor</a>.</p>

<figure class="zoomable-200pct">
  <img src="/assets/images/cant-delete-vhd-apimonitor.png" alt="APIMonitor showing the WMI Calls from the Hyper-V Disk Wizard." /><figcaption>
      Fig 4. Rohitab API Monitor.

    </figcaption></figure>

<figure class="zoomable-200pct">
  <img src="/assets/images/cant-delete-vhd-apimonitor2.png" alt="APIMonitor showing the parameter object creation." /><figcaption>
      Fig 4. The Hyper-V Wizard Generating its parameters.

    </figcaption></figure>

<p class="notice"><strong>Aside</strong>
I just want to point out how amazingly handy this tool is. It goes much deeper than procmon/sysmon/WPA/WPP traces (unless you somehow manage to get the Microsoft private symbols/TMFs) and has a ton of accellerators that make ‘getting the job done’ faster than if you have to bust out dnSpy/dotpeek/windbg/ida/etc. In this case it’s automatically capturing the contents of the objects in the parameter pointers and saving them. No manual breakpoint necessary. I’ve even used API Monitor to hijack a firmware update tool and edit it’s ioctls on the fly to force it to update a device that the OEM didn’t want updated. Long story.</p>

<p class="notice"><strong>Aside-Aside</strong>
Sorry for the zoom &amp; swoop effect, middle ground between “open full image in new tab” and “fancy overlay with buttons and stuff”.</p>

<p><a href="https://docs.microsoft.com/en-us/windows/win32/wmisdk/--parameters">You can see the documentation on how you pass parameters to a WMI Method here</a> but the gist is that the wizard uses GetMethod to have WMI build you the __PARAMETERS object and PutMethod to populate the properties, then when ExecMethodAsync is called you just give it the pointer to your populated object. API monitor lets you see what values the Wizard is packing into that object before the call happens.</p>

<p>A quick analysis shows that mmc is building the request with RequestedState of 4 and a TimeoutPeriod of null. I’m totally certain that we tried that earlier (<strong>Narrator: We didn’t.</strong>) it must be something else going on. Let’s see if we can manually cancel a VHD creation started from the wizard!</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$tjob</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s1">'InstanceID = "GUID-INSTANCE-ID-FROM-MMC"'</span><span class="w">
</span><span class="nv">$tjob</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="p">}</span><span class="w">

</span><span class="n">ReturnValue</span><span class="w"> </span><span class="nx">PSComputerName</span><span class="w">
</span><span class="o">-----------</span><span class="w"> </span><span class="o">--------------</span><span class="w">
       </span><span class="mi">4096</span><span class="w">

</span></code></pre></div></div>

<p>4096 isn’t listed as a storage job result but looking at the CIM_ConcreteJob docs that’s <code class="language-plaintext highlighter-rouge">Method Parameters Checked - Transition Started (4096)</code>! It worked! So why can’t we cancel the VHD creation started via powershell? The two CIM jobs appeared identical!</p>

<p>Oh no…</p>

<p><em>Oh no…</em></p>

<p>Guess what combination of RequestedState’s and TimeoutPeriod’s we didn’t try?!</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$jobc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s1">'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'</span><span class="w">
</span><span class="nv">$jobc</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="p">}</span><span class="w">

</span><span class="n">ReturnValue</span><span class="w"> </span><span class="nx">PSComputerName</span><span class="w">
</span><span class="o">-----------</span><span class="w"> </span><span class="o">--------------</span><span class="w">
       </span><span class="mi">4096</span><span class="w">
</span></code></pre></div></div>

<p>If I had tried a few different parameter combinations off the bat (honestly there weren’t that many) I would’ve probably figured this out 90 minutes ago. <strong>On the other hand, if this had been documented in any way I wouldn’t have had to guess all of that</strong>.</p>

<h1 id="how-to-cancel-a-fixed-vhd-creation-started-by-new-vhd">How to cancel a fixed VHD creation started by New-VHD</h1>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="c"># Step 1. Find the InstanceID of your job</span><span class="w">
</span><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select</span><span class="w"> </span><span class="nx">InstanceID</span><span class="p">,</span><span class="w"> </span><span class="nx">Caption</span><span class="p">,</span><span class="w"> </span><span class="nx">Description</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">,</span><span class="w"> </span><span class="nx">Status</span><span class="p">,</span><span class="w"> </span><span class="nx">StatusDescriptions</span><span class="p">,</span><span class="w"> </span><span class="nx">JobStatus</span><span class="p">,</span><span class="w"> </span><span class="nx">PercentComplete</span><span class="p">,</span><span class="w"> </span><span class="nx">Cancellable</span><span class="w">

</span><span class="c">#I only had one job running so it was easy enough to figure out.</span><span class="w">
</span><span class="n">InstanceID</span><span class="w">         </span><span class="p">:</span><span class="w"> </span><span class="nx">D65CC6A8-E7B4-45E4-A13D-CF7BAD642912</span><span class="w">
</span><span class="n">Caption</span><span class="w">            </span><span class="p">:</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w"> </span><span class="nx">Creation</span><span class="w">
</span><span class="n">Description</span><span class="w">        </span><span class="p">:</span><span class="w"> </span><span class="nx">Creating</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w">
</span><span class="n">Name</span><span class="w">               </span><span class="p">:</span><span class="w"> </span><span class="nx">Virtual</span><span class="w"> </span><span class="nx">Hard</span><span class="w"> </span><span class="nx">Disk</span><span class="w"> </span><span class="nx">Creation</span><span class="w">
</span><span class="n">Status</span><span class="w">             </span><span class="p">:</span><span class="w"> </span><span class="nx">OK</span><span class="w">
</span><span class="n">StatusDescriptions</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="n">Job</span><span class="w"> </span><span class="nx">is</span><span class="w"> </span><span class="nx">running</span><span class="p">}</span><span class="w">
</span><span class="n">JobStatus</span><span class="w">          </span><span class="p">:</span><span class="w"> </span><span class="nx">Job</span><span class="w"> </span><span class="nx">is</span><span class="w"> </span><span class="nx">running</span><span class="w">
</span><span class="n">PercentComplete</span><span class="w">    </span><span class="p">:</span><span class="w"> </span><span class="nx">7</span><span class="w">
</span><span class="n">Cancellable</span><span class="w">        </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">


</span><span class="c"># Step 2. Grab and cancel the Job</span><span class="w">
</span><span class="nv">$jobc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-CimInstance</span><span class="w"> </span><span class="nt">-Namespace</span><span class="w"> </span><span class="nx">root\virtualization\v2</span><span class="w"> </span><span class="nt">-ClassName</span><span class="w"> </span><span class="nx">CIM_ConcreteJob</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s1">'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'</span><span class="w">
</span><span class="nv">$jobc</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-CimMethod</span><span class="w"> </span><span class="nt">-MethodName</span><span class="w"> </span><span class="nx">RequestStateChange</span><span class="w"> </span><span class="nt">-Arguments</span><span class="w"> </span><span class="p">@{</span><span class="nx">RequestedState</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="nx">TimeoutPeriod</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="p">}</span><span class="w">

</span></code></pre></div></div>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[If you CTRL-C the New-VHD cmdlet the CIM Job will keep running in the background and prevent you from deleting the disk.]]></summary></entry><entry><title type="html">Office 365/Apps for Enterprise sign-in page blank</title><link href="https://blog.teknikcs.ca/2022/02/14/office-365-apps-for-enterprise-sign-in-page-blank" rel="alternate" type="text/html" title="Office 365/Apps for Enterprise sign-in page blank" /><published>2022-02-14T00:00:00+00:00</published><updated>2022-02-14T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2022/02/14/office-365-apps-for-enterprise-sign-in-page-blank</id><content type="html" xml:base="https://blog.teknikcs.ca/2022/02/14/office-365-apps-for-enterprise-sign-in-page-blank"><![CDATA[<p class="notice--tlwr">TL;WR: If your WAM sign-in screens for office only have the copyright statement then the issue might be that the WAM modal is being rendered in an ancient IE compatability mode. I don’t know what the office/proper/long term fix is, but you can use one of the hacks below to bypass the issue temporarily.<br /><a href="#option-a-featurecontrol-registry-fix">Click here to skip to the answer</a></p>

<ul>
  <li>Are you having fun with blank sign-in screens for basically any Microsoft app that uses the web authentication pop up? (Office, Visual Studio, etc.)</li>
  <li>Do you see a tiny “©yyyy Microsoft Privacy &amp; Cookies” at the bottom of a white/blank sign in popup?</li>
  <li><a href="https://docs.microsoft.com/en-us/office365/troubleshoot/o365-admin-welcome">Did you look at every 🤬 page in the O365 Administration Troubleshooting Site</a>?</li>
  <li>Did you reset activation and use the sign in troubleshooter/SaRA?</li>
  <li>Did you <em>then</em> do the inetcpl reset?</li>
  <li>Are you aware of (and annoyed by) the wrong, outdated, and irrelevant advice to disable ADAL, WAM, etc.?</li>
  <li><del>Did you DISM and SFC?</del> Just kidding.</li>
</ul>

<p>If the answer to all of the above questions is yes, then you’re as desperate as I was and there’s a small chance I have the answer for you! I’m <em>supposed</em> to advise you to do the ‘supported thing’ which is reinstall windows… but if you’re reading this then you probably don’t care!</p>

<p>Fortunately for you this isn’t Microsoft <em>“Answers”</em> and I have two ways to get the page working: skip to Option A or B if you’re feeling <strong>bold</strong>.</p>

<h2 id="background">Background</h2>

<p>At first I thought that maybe it was a JavaScript issue, maybe bad zone security settings, etc. I used fiddler to scrape the URL that office was trying to load for the sign in and found something along the lines of <a href="https://odc.officeapps.live.com/odc/v2.1/hrd">“https://odc.officeapps.live.com/odc/v2.1/hrd”</a> (plus a bunch of queries) and bingo! This was the page that WAM was trying to load. It works in every browser *except* IE… which happens to be the engine that O365 apps use to render the login page depending on OS build, App build, and whether Mecury is in retrograde.</p>

<p><a href="https://docs.microsoft.com/en-us/office/dev/add-ins/concepts/browsers-used-by-office-web-add-ins"><em>Note: You can/should check here to see which engine the sign-in modal might be using.</em></a></p>

<p>Digging further I saw a bunch of strange issues in the JS console and on a whim changed the emulation settings. You can test this yourself pretty easily:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">iexplore.exe https://odc.officeapps.live.com/odc/v2.1/hrd</code></li>
  <li>The page should be nothing but a color gradient and a little footer</li>
  <li>Open devtools (F12)</li>
  <li>Go to the emulation tab and change Document Mode to 11</li>
</ol>

<p>If the prompt suddenly works then I think we’re on the same page?! If you look in the source you can see that Microsoft specifies that it wants IE to use IE10 mode with the <code class="language-plaintext highlighter-rouge">x-ua-compatible</code> tag. I’ll be damned if I know why they set that, but I *do* know that IE11 mode is the one that actually works.</p>

<figure class="">
  <img src="/wp-content/uploads/2022/02/image-4.png" alt="Screenshot of the page body showing a header meta tag setting content=&quot;ie=10&quot;" /><figcaption>
      Fig 1. A unconscionable default.

    </figcaption></figure>

<p>So now we’re <a href="https://en.wiktionary.org/wiki/sophomore#Etymology">sophomores</a>: We’ve learned that IE is trying (and failing) to render this in IE10 mode and that it works correctly in Edge mode. We are still foolish as to the reason this is only happening to us and not a few million other companies (my first guess would probably be something vague like “Borked IE registry”). Fortunately we’re now wise to a potential workaround: force the embedded IE window to render the page in Edge mode.</p>

<h2 id="option-a-featurecontrol-registry-fix">Option A) FeatureControl Registry “Fix”</h2>

<p>We’re going to use a registry flag as per <a href="https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/general-info/ee330730(v=vs.85)?redirectedfrom=MSDN">MSDN</a> to tell IE that it should ignore all hints and render everything that your target application requests in IE11 mode.</p>

<figure class="">
  <img src="/wp-content/uploads/2022/02/image-1.png" alt="Screenshot of regedit expanded to HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION" /><figcaption>
      Fig 2. An ugly hack.

    </figcaption></figure>

<ol>
  <li>Pick an app you’re going to use for sign in (OneNote, Word, Excel, etc.)</li>
  <li>Go to <code class="language-plaintext highlighter-rouge">HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION</code></li>
  <li>Create a new <code class="language-plaintext highlighter-rouge">REG_DWORD</code> with the name of your chosen app (e.g. OneNote.exe), set the value to <code class="language-plaintext highlighter-rouge">0x00002af9 (11001)</code>.</li>
  <li>Relaunch your app and sign in! Congrats!</li>
  <li><strong>GO BACK AND DELETE THE KEY! Don’t be lazy, do it now, otherwise you’re going to tear your hair out when something dumb breaks in the future and you’ve forgotten all about this.</strong></li>
</ol>

<h2 id="option-b-ugly-fiddler-mitm-hackfix">Option B) Ugly Fiddler MITM <del>Hack</del>“Fix”</h2>

<p>Well, just use Fiddler (Or Charles) and edit the contents of the page before it lands in the target application. We’re going to change the hint from <code class="language-plaintext highlighter-rouge">content="ie=10"</code> to <code class="language-plaintext highlighter-rouge">content="EdgeHTML</code>” <a href="https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/jj676915(v=vs.85)">which will tell IE to render the page in IE11/Edge mode</a>.</p>

<figure class="">
  <img src="/wp-content/uploads/2022/02/image-2.png" alt="Screenshot of the noted FiddlerScript" /><figcaption>
      Fig 3. A substantially uglier hack.

    </figcaption></figure>

<ol>
  <li>Open fiddler with HTTPS interception enabled.</li>
  <li>Open the FiddlerScript tab, then go to the <code class="language-plaintext highlighter-rouge">OnBeforeResponse</code> function.</li>
  <li>Add this line to the function <code class="language-plaintext highlighter-rouge">oSession.utilReplaceInResponse("ie=10","EdgeHTML");</code></li>
  <li>Relaunch your app and sign in! More congrats!</li>
  <li><strong>GO BACK AND REMOVE THAT LINE FROM THE FIDDLERSCRIPT TAB!</strong> <strong>We both know that you’ll forget and the next time you’re using fiddler you’ll waste 3 hours of troubleshooting before you remember that this line is running.</strong> It has no conditions or anything, it’s what you might generously call <em>enthusiastic programming</em>.</li>
</ol>

<p>One one hand this doesn’t fix the root cause because I don’t know what the root cause ACTUALLY IS. On the other hand it does finally let you sign into your 365/Visual Studio apps. If you have any ideas what the heck is going on please do share!</p>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[Possible cause and hackey temp-fix for the Office 365/Apps for Ent. WAM sign in page being almost completely blank.]]></summary></entry><entry><title type="html">VBA Dumb Problem of the Day</title><link href="https://blog.teknikcs.ca/2021/02/26/vba-dumb-problem-of-the-day" rel="alternate" type="text/html" title="VBA Dumb Problem of the Day" /><published>2021-02-26T00:00:00+00:00</published><updated>2021-02-26T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2021/02/26/vba-dumb-problem-of-the-day</id><content type="html" xml:base="https://blog.teknikcs.ca/2021/02/26/vba-dumb-problem-of-the-day"><![CDATA[<p>Sure, many of us loathe VBA, but we still have to support it. I was trying to update an Excel macro that configured a print layout and suddenly I was unable to insert page breaks. “It was working a second ago”.</p>

<blockquote>
  <p>Run-time error ‘1004’: unable to set the PageBreak property of the Range class</p>
</blockquote>

<p>Turns out that when applied to my test data the PrintArea of the print layout was set so that the first row I was trying to add a page break to was also the first row of the PrintArea. I guess it makes sense but that isn’t documented anywhere.</p>

<p>So if you’ve been trying a dozen different ways of adding a page break, do yourself a favour and double check the area selected for printing. If you’re trying to add a page break on the border of your selection it’ll fail.</p>]]></content><author><name>James Foley</name></author><category term="Uncategorized" /><summary type="html"><![CDATA[Easy to miss behaviour when inserting page breaks in an excel document via VBA.]]></summary></entry><entry><title type="html">Scheduled Powershell Scripts without storing credentials</title><link href="https://blog.teknikcs.ca/2020/09/12/scheduled-powershell-scripts-without-storing-credentials" rel="alternate" type="text/html" title="Scheduled Powershell Scripts without storing credentials" /><published>2020-09-12T00:00:00+00:00</published><updated>2020-09-12T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2020/09/12/scheduled-powershell-scripts-without-storing-credentials</id><content type="html" xml:base="https://blog.teknikcs.ca/2020/09/12/scheduled-powershell-scripts-without-storing-credentials"><![CDATA[<p class="notice--tlwr">TL;WR: Use a G/MSA and a Base64 encoded task to execeute a script as a service account in a scheduled task without manually saving credentials or leaving files.</p>

<p class="notice--warning">Note: The following isn’t normally a good practice for a lot of reasons. In fact (minus the G/MSA account) it’s practically a low-effort malware persistence technique. Normally you’d want a repository style folder so that you can view/track the scripts (and potentially include them in your CD pipelines).<br />This <em>could</em> be appropriate if your already-change-controlled scripts need to create temporary sub-tasks/jobs. Even then, SECOPS will probably raise their eyebrows at you for suggesting it.</p>

<p>Sometimes I want to schedule a script to run <em>with</em> specific domain credentials/service account <em>without</em> storing credentials locally. With G/MSA’s an attacker needs to use AD/LDAP to steal the G/MSA password blob, which is easier to log and identify centrally. Credentials saved to a scheduled task can be really easily extracted with something like <a href="http://www.nirsoft.net/utils/network_password_recovery.html">NetPass</a>.</p>

<p>Anyways, digressing from the caviats: here I wanted to schedule maintenance notifications to users that have been logged in for so long that their hosts are up for replacement as part of our lifecycle automations, but wanted to put together a generic helper utility for creating these tasks.</p>

<p>You can’t select a G/MSA account in the Task Scheduler UI, only with SC or PowerShell; and since nobody like a process that reads “Do a dozen things by hand in the UI, then write some lines to modify it” I figured the generic helper would be a lot more useful.</p>

<p>The next step to low footprint bliss is to say goodbye to all the files and ACLs! Inline the scripts (if they’re short enough)! Here are the key parts for a file-less, sort-of-credential-less PowerShell scheduled task. In this case it just schedules desktop messages for Citrix sessions. Ironically this specific example drops transcript copies but you get the point.</p>

<p>(Sorry for not sharing the full helper, but these are the guts of it. Pretty simple to generalize based on your needs. Besides, I’d feel bad if someone out there is B64 encoding <em>all</em> of their maintenance scripts.)</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Encode script as Base64, send a Citrix message in this example</span><span class="w">
</span><span class="kr">function</span><span class="w"> </span><span class="nf">EncodeMessageTaskScript</span><span class="w"> </span><span class="p">(</span><span class="nv">$MessageText</span><span class="p">,</span><span class="w"> </span><span class="nv">$AdminAddress</span><span class="p">)</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="nv">$TaskScript</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sh">@"
    Start-Transcript "C:\ScriptLogs\ScheduledPS.log" -Append
    &amp; { Add-Pssnapin @('Citrix.Host.Admin.V2','Citrix.Broker.Admin.V2')}

    </span><span class="se">`$</span><span class="sh">CurrentSessions = Get-BrokerSession -AdminAddress "</span><span class="nv">$AdminAddress</span><span class="sh">" -MaxRecordCount 1000 | ? DesktopGroupName -eq 'Nope'
    </span><span class="se">`$</span><span class="sh">CurrentSessions | % { Send-BrokerSessionMessage -AdminAddress "</span><span class="nv">$AdminAddress</span><span class="sh">" -InputObject </span><span class="se">`$</span><span class="sh">_ -MessageStyle Critical -Text "</span><span class="nv">$MessageText</span><span class="sh">"</span><span class="se">`n</span><span class="sh">Message Sent [</span><span class="se">`$</span><span class="sh">(Get-Date)]" -Title "Maintenance Warning"}

    Stop-Transcript

"@</span><span class="w"> </span><span class="c">#Let's pretend that this is indented...</span><span class="w">

    </span><span class="p">[</span><span class="n">Convert</span><span class="p">]::</span><span class="n">ToBase64String</span><span class="p">([</span><span class="n">Text.Encoding</span><span class="p">]::</span><span class="n">Unicode.GetBytes</span><span class="p">(</span><span class="nv">$TaskScript</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="kr">function</span><span class="w"> </span><span class="nf">CreateScheduledGMSATask</span><span class="w"> </span><span class="p">(</span><span class="nv">$EncodedPsScript</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">datetime</span><span class="p">]</span><span class="nv">$TriggerDateTime</span><span class="p">,</span><span class="w"> </span><span class="nv">$TaskName</span><span class="p">)</span><span class="w">
</span><span class="p">{</span><span class="w">

    </span><span class="nv">$Action</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-ScheduledTaskAction</span><span class="w"> </span><span class="nt">-Execute</span><span class="w"> </span><span class="s2">"powershell.exe"</span><span class="w"> </span><span class="nt">-Argument</span><span class="w"> </span><span class="s2">"-EncodedCommand </span><span class="se">`"</span><span class="nv">$EncodedPsScript</span><span class="se">`"</span><span class="s2"> -NoLogo -NoProfile -ExecutionPolicy Bypass"</span><span class="w">
    </span><span class="nv">$Principal</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-ScheduledTaskPrincipal</span><span class="w"> </span><span class="nt">-LogonType</span><span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="nt">-RunLevel</span><span class="w"> </span><span class="nx">Limited</span><span class="w"> </span><span class="nt">-UserId</span><span class="w"> </span><span class="s1">'DOMAIN\[G]MSA$'</span><span class="w">
    </span><span class="nv">$Settings</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-ScheduledTaskSettingsSet</span><span class="w"> </span><span class="nt">-Compatibility</span><span class="w"> </span><span class="nx">Win8</span><span class="w">
    </span><span class="nv">$Trigger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-ScheduledTaskTrigger</span><span class="w"> </span><span class="nt">-Once</span><span class="w"> </span><span class="nt">-At</span><span class="w"> </span><span class="nv">$TriggerDateTime</span><span class="w">

    </span><span class="nv">$TaskObj</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-ScheduledTask</span><span class="w"> </span><span class="nt">-Action</span><span class="w"> </span><span class="nv">$Action</span><span class="w"> </span><span class="nt">-Principal</span><span class="w"> </span><span class="nv">$Principal</span><span class="w"> </span><span class="nt">-Trigger</span><span class="w"> </span><span class="nv">$Trigger</span><span class="w"> </span><span class="nt">-Settings</span><span class="w"> </span><span class="nv">$Settings</span><span class="w">
    </span><span class="n">Register-ScheduledTask</span><span class="w"> </span><span class="nt">-TaskName</span><span class="w"> </span><span class="nv">$TaskName</span><span class="w"> </span><span class="nt">-InputObject</span><span class="w"> </span><span class="nv">$TaskObj</span><span class="w">

</span><span class="p">}</span><span class="w">

</span><span class="nv">$EncodedReminderTask</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">EncodeMessageTaskScript</span><span class="w"> </span><span class="nt">-MessageText</span><span class="w"> </span><span class="s2">"Your Message Text"</span><span class="w"> </span><span class="nt">-AdminAddress</span><span class="w"> </span><span class="nv">$CitrixAdminAddress</span><span class="w">
</span><span class="n">CreateScheduledGMSATask</span><span class="w"> </span><span class="nt">-EncodedPsScript</span><span class="w"> </span><span class="nv">$EncodedReminderTask</span><span class="w"> </span><span class="nt">-TriggerDateTime</span><span class="w"> </span><span class="nv">$MaintReminder</span><span class="w"> </span><span class="nt">-TaskName</span><span class="w"> </span><span class="s2">"MessageTask </span><span class="si">$(</span><span class="nv">$MaintStartTime</span><span class="o">.</span><span class="nf">ToString</span><span class="p">(</span><span class="s1">'yyyyMMdd.HHmmss'</span><span class="p">)</span><span class="s2">)"</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>James Foley</name></author><category term="Powershell" /><summary type="html"><![CDATA[Thoughts on *simple* scheduled scripts without needing files or unprotected credentials.]]></summary></entry><entry><title type="html">DPM Scripted VM Recovery Fails (Error 104 0x80041002, 3111)</title><link href="https://blog.teknikcs.ca/2020/04/16/dpm-scripted-vm-recovery-fails-error-104-0x80041002-3111" rel="alternate" type="text/html" title="DPM Scripted VM Recovery Fails (Error 104 0x80041002, 3111)" /><published>2020-04-16T00:00:00+00:00</published><updated>2020-04-16T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2020/04/16/dpm-scripted-vm-recovery-fails-error-104-0x80041002-3111</id><content type="html" xml:base="https://blog.teknikcs.ca/2020/04/16/dpm-scripted-vm-recovery-fails-error-104-0x80041002-3111"><![CDATA[<p class="notice--tlwr">TL;WR: If you’re trying to use DPM to restore a copy of a protected hyper-v datasource to a folder instead of to a hypervisor (e.g. so that you can mount and examine the VHD) then you need to use <code class="language-plaintext highlighter-rouge">-RecoveryLocation CopyToFolder -RecoveryType Restore</code>.</p>

<blockquote>
  <p>-RecoveryType</p>

  <p>Specifies the recovery type. If you specify the <em>HyperVDatasource</em> parameter, the only valid value is Recover. The acceptable values for this parameter are: Recover or Restore.</p>

  <p><a href="https://docs.microsoft.com/en-us/powershell/module/dataprotectionmanager/new-dpmrecoveryoption?view=systemcenter-ps-2019">https://docs.microsoft.com/en-us/powershell/module/dataprotectionmanager/new-dpmrecoveryoption?view=systemcenter-ps-2019</a></p>
</blockquote>

<p>The Microsoft documentation is flat out wrong. It very explicitly states that the only valid <code class="language-plaintext highlighter-rouge">RecoveryType</code> for <code class="language-plaintext highlighter-rouge">HyperVDatasource</code> is <code class="language-plaintext highlighter-rouge">Recover</code>. When trying to recover to an alternate disk location their example does not work. Based on the example script you would expect the code below to work. Instead if you try it you’ll get a powershell error stating <code class="language-plaintext highlighter-rouge">"The recovery point location that you have passed is invalid. Please try again with a different value (ID:31050)."</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$BadOption</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-DPMRecoveryOption</span><span class="w"> </span><span class="nt">-HyperVDatasource</span><span class="w"> </span><span class="nt">-TargetServer</span><span class="w"> </span><span class="s2">"target.contoso.com"</span><span class="w"> </span><span class="nt">-RecoveryLocation</span><span class="w"> </span><span class="nx">CopyToFolder</span><span class="w"> </span><span class="nt">-RecoveryType</span><span class="w"> </span><span class="nx">Recover</span><span class="w"> </span><span class="nt">-TargetLocation</span><span class="w"> </span><span class="s2">"D:\DestinationFolder"</span><span class="w">
</span></code></pre></div></div>
<p>So maybe instead of that you’d google around then try the following (obviously wrong) option <code class="language-plaintext highlighter-rouge">AlternateHyperVServer</code> out of desperation and it appears to work… At first.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$BadOption</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-DPMRecoveryOption</span><span class="w"> </span><span class="nt">-HyperVDatasource</span><span class="w"> </span><span class="nt">-TargetServer</span><span class="w"> </span><span class="s2">"target.contoso.com"</span><span class="w"> </span><span class="nt">-RecoveryLocation</span><span class="w"> </span><span class="nx">AlternateHyperVServer</span><span class="w"> </span><span class="nt">-RecoveryType</span><span class="w"> </span><span class="nx">Recover</span><span class="w"> </span><span class="nt">-TargetLocation</span><span class="w"> </span><span class="s2">"D:\DestinationFolder"</span><span class="w">
</span></code></pre></div></div>
<p>However at some point that job will fail with <code class="language-plaintext highlighter-rouge">"An unexpected error occurred while the job was running. (ID 104 Details: Unknown error (0x80041002) (0x80041002))"</code> which is entirely unhelpful. If you go to the job details you’ll get an equally unhelpful <code class="language-plaintext highlighter-rouge">error 3111</code>. Making some assumptions around that error code (<code class="language-plaintext highlighter-rouge">WMI object not found error</code>) I’m thinking that it’s trying to import the VM to a hyper-v instance running on that server. That doesn’t work if there’s no valid hypervisor running. Instead you need to user the parameters <code class="language-plaintext highlighter-rouge">-RecoveryLocation CopyToFolder</code> and <code class="language-plaintext highlighter-rouge">-RecoveryType Restore</code>.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$WorkingOption</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-DPMRecoveryOption</span><span class="w"> </span><span class="nt">-HyperVDatasource</span><span class="w"> </span><span class="nt">-TargetServer</span><span class="w"> </span><span class="s2">"target.contoso.com"</span><span class="w"> </span><span class="nt">-RecoveryLocation</span><span class="w"> </span><span class="nx">CopyToFolder</span><span class="w"> </span><span class="nt">-RecoveryType</span><span class="w"> </span><span class="nx">Restore</span><span class="w"> </span><span class="nt">-TargetLocation</span><span class="w"> </span><span class="s2">"D:\DestinationFolder"</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>James Foley</name></author><category term="System Center" /><summary type="html"><![CDATA[Microsoft's DPM documentation is wrong regarding the recovery methods for Hyper-V VM's...]]></summary></entry><entry><title type="html">Miscellanea</title><link href="https://blog.teknikcs.ca/2020/01/28/miscellanea" rel="alternate" type="text/html" title="Miscellanea" /><published>2020-01-28T00:00:00+00:00</published><updated>2020-01-28T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2020/01/28/miscellanea</id><content type="html" xml:base="https://blog.teknikcs.ca/2020/01/28/miscellanea"><![CDATA[<h1 id="cloud">Cloud</h1>
<p>The Well Architected Framework (<a href="https://docs.microsoft.com/en-us/azure/architecture/framework/">https://docs.microsoft.com/en-us/azure/architecture/framework</a>)</p>

<p>The Cloud Adoption Framework (<a href="https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/">https://docs.microsoft.com/en-us/azure/cloud-adoption-framework</a>)</p>

<ul>
  <li>Handy Shortcut to the 5R’s (<a href="https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/digital-estate/5-rs-of-rationalization">https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/digital-estate/5-rs-of-rationalization</a>)</li>
</ul>

<h1 id="other">Other</h1>

<p>Joel On Software (<a href="https://www.joelonsoftware.com/">https://www.joelonsoftware.com</a>)</p>

<p>Azure Architecture Templates (<a href="https://docs.microsoft.com/en-us/azure/architecture/browse/">https://docs.microsoft.com/en-us/azure/architecture/browse</a>)</p>

<p>Azure AD Teams and Groups drawings (<a href="https://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters#groups-in-microsoft-365">https://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters#groups-in-microsoft-365</a>)</p>

<p>SANS/ISC Daily Security Briefing (<a href="https://podcasts.apple.com/us/podcast/sans-internet-stormcenter-daily-cyber-security-podcast/id304863991">https://podcasts.apple.com/us/podcast/sans-internet-stormcenter-daily-cyber-security-podcast/id304863991</a>)</p>

<p>The Twelve-Factor App (<a href="https://12factor.net/">https://12factor.net</a>)</p>

<p>The Eight Fallacies of Distributed Computing (<a href="http://nighthacks.com/jag/res/Fallacies.html">http://nighthacks.com/jag/res/Fallacies.html</a>)</p>

<p>Archived Ten Laws of Security 2.0 (<a href="https://web.archive.org/web/20180529154650/https://technet.microsoft.com/en-us/library/hh278941.aspx">https://web.archive.org/web/20180529154650/https://technet.microsoft.com/en-us/library/hh278941.aspx</a>)</p>

<p>Archived Ten Laws response (<a href="https://web.archive.org/web/20190928204316/http://www.edgeblog.net/2006/10-new-immutable-laws-of-it-security/">https://web.archive.org/web/20190928204316/http://www.edgeblog.net/2006/10-new-immutable-laws-of-it-security</a>)</p>

<p>Archived Ten Laws Re-Review (<a href="https://web.archive.org/web/20190710001511/https://docs.microsoft.com/en-us/previous-versions/technet-magazine/cc895640(v=msdn.10)">https://web.archive.org/web/20190710001511/https://docs.microsoft.com/en-us/previous-versions/technet-magazine/cc895640(v=msdn.10)</a>)</p>

<p>Krebs on Security (<a href="https://krebsonsecurity.com/">https://krebsonsecurity.com</a>)</p>

<p>Raymond Chen’s Blog (<a href="https://devblogs.microsoft.com/oldnewthing/">https://devblogs.microsoft.com/oldnewthing</a>)</p>

<p>Barracuda Spam Firewall Rooting (<a href="http://blog.shiraj.com/2009/09/barracuda-spam-firewall-root-password/">http://blog.shiraj.com/2009/09/barracuda-spam-firewall-root-password</a>)</p>

<p>Group Policy team blog (<a href="https://blogs.technet.microsoft.com/grouppolicy/">https://blogs.technet.microsoft.com/grouppolicy</a>)</p>

<p>Aaron Stebner’s Weblog (notes on .Net) (<a href="https://docs.microsoft.com/en-ca/archive/blogs/astebner/">https://docs.microsoft.com/en-ca/archive/blogs/astebner</a>)</p>

<p>AskPerf Ask The Performance Team (<a href="https://aka.ms/AskPerf">https://aka.ms/AskPerf</a>)</p>

<p>AskDS Ask the Directory Services Team (<a href="https://aka.ms/AskDS">https://aka.ms/AskDS</a>) (Archive: <a href="https://docs.microsoft.com/en-ca/archive/blogs/askds/">https://docs.microsoft.com/en-ca/archive/blogs/askds</a>) (A lot of interesting deep dives on ESE)</p>

<p>Thomas Maurer’s Blog (Azure Advocate) (<a href="https://www.thomasmaurer.ch/">https://www.thomasmaurer.ch</a>)</p>

<p>Carl Stalhood’s EUC Blog (<a href="https://www.carlstalhood.com/">https://www.carlstalhood.com</a>)</p>

<p>Robin Hobo (<a href="https://www.robinhobo.com/">https://www.robinhobo.com</a>)</p>

<p>Helge Klein’s Blog (<a href="https://helgeklein.com/">https://helgeklein.com</a>)</p>

<p>Brent Ozar’s Corp Blog (<a href="https://www.brentozar.com/">https://www.brentozar.com</a>)</p>

<p>DBA Reactions (Lighthearted fun) (<a href="https://dbareactions.com/">https://dbareactions.com</a>)</p>]]></content><author><name>James Foley</name></author><category term="Everything Else" /><summary type="html"><![CDATA[Miscellaneous links.]]></summary></entry><entry><title type="html">Error 0x8009030E Trying to Migrate VM in System Center VMM</title><link href="https://blog.teknikcs.ca/2019/10/18/error-0x8009030e-trying-to-migrate-vm-in-system-center-vmm" rel="alternate" type="text/html" title="Error 0x8009030E Trying to Migrate VM in System Center VMM" /><published>2019-10-18T00:00:00+00:00</published><updated>2019-10-18T00:00:00+00:00</updated><id>https://blog.teknikcs.ca/2019/10/18/error-0x8009030e-trying-to-migrate-vm-in-system-center-vmm</id><content type="html" xml:base="https://blog.teknikcs.ca/2019/10/18/error-0x8009030e-trying-to-migrate-vm-in-system-center-vmm"><![CDATA[<p>Things to double check when working with live migrations in VMM 2016:</p>

<blockquote>
  <p>Error (23008)</p>

  <p>The VM BlahBlahBlah cannot be migrated to Host BlahHost.contoso.ads due to incompatibility issues. The Virtual Machine Management Service failed to establish a connection for a Virtual Machine migration with host ‘BlahHost.contoso.ads’: No credentials are available in the security package (0x8009030E).</p>
</blockquote>

<ol>
  <li>Double check that hosts were setup with the correct Kerberos delegation settings
    <ol>
      <li>Ensure it’s set to Kerberos only, others say this doesn’t work but I *think* that they were just too lazy to wait for the ticket refresh. You just have to wait a few minutes after doing <code class="language-plaintext highlighter-rouge">klist purge -li 0x3E7</code> to clear the computer account tickets on each host and it will start working. No point disabling a security feature out of impatience.</li>
    </ol>
  </li>
  <li>Double check that our VMM management account was setup under Host Access &gt; Host management credentials &gt; Run As Account.</li>
  <li>Double check that hosts are configured to use Kerberos as their Live Migration method</li>
</ol>

<p><img src="/wp-content/uploads/2019/09/image.png" alt="" /></p>]]></content><author><name>James Foley</name></author><category term="System Center" /><summary type="html"><![CDATA[What to double check (you did a first check right?) when live migrations are failing due to credential issues.]]></summary></entry></feed>