<?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: Miro</title>
    <description>The latest articles on DEV Community by Miro (@milolav).</description>
    <link>https://dev.to/milolav</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%2F287202%2F1d03bb8f-6eca-4e40-8802-311ab5c261ab.png</url>
      <title>DEV Community: Miro</title>
      <link>https://dev.to/milolav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/milolav"/>
    <language>en</language>
    <item>
      <title>Oauth2 certificate authentication in bash script</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Mon, 08 Mar 2021 01:00:08 +0000</pubDate>
      <link>https://dev.to/milolav/oauth2-certificate-authentication-in-bash-script-3b1e</link>
      <guid>https://dev.to/milolav/oauth2-certificate-authentication-in-bash-script-3b1e</guid>
      <description>&lt;p&gt;I was exploring the possibility of getting the auth token in bash using certificate authentication. Here are two examples how to obtain access token, one for Microsoft Graph and the other one for Google APIs.&lt;/p&gt;

&lt;p&gt;Both scripts are similar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create jwt payload/claims&lt;/li&gt;
&lt;li&gt;sign header +payload with private key of the certificate uploaded to Microsoft/Google&lt;/li&gt;
&lt;li&gt;post data to token endpoint&lt;/li&gt;
&lt;li&gt;acquire access_token &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A bit of explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;base64 -w 0 | tr '/+' '_-' | tr -d '='&lt;/code&gt; - this is simple base64url encoding
&lt;code&gt;base64 -w 0&lt;/code&gt; means that data won't be broken in multiple lines, &lt;code&gt;tr '/+' '_-'&lt;/code&gt; is used to replace &lt;code&gt;/&lt;/code&gt; with &lt;code&gt;_&lt;/code&gt; and &lt;code&gt;+&lt;/code&gt; with &lt;code&gt;-&lt;/code&gt;, and finally &lt;code&gt;tr -d '='&lt;/code&gt; is used to remove &lt;code&gt;=&lt;/code&gt; which is standard base64 padding&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openssl dgst -sha256 -sign $key_file&lt;/code&gt; is signing input with certificate key stored in the &lt;code&gt;$key_file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;date +%s&lt;/code&gt; is current timestamp, and &lt;code&gt;$(($ts + 600))&lt;/code&gt; adds 10 minutes of token validity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft uses certificate fingerprint (thumbprint) in jwt header and it is a binary value encoded as base64url.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openssl x509 -fingerprint -noout -in $cert_file&lt;/code&gt; will produce something like
&lt;code&gt;SHA1 Fingerprint=AA:BB:CC:DD:EE...&lt;/code&gt; after which &lt;code&gt;cut -d '=' -f 2&lt;/code&gt; is used to get everything right of the &lt;code&gt;=&lt;/code&gt; sign (the fingerprint), which is then converted into binary using &lt;code&gt;xxd -r -p&lt;/code&gt;  and finally converted into base64url&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also uses "a Guid" for unique identifier of a jwt where &lt;code&gt;cat /proc/sys/kernel/random/uuid&lt;/code&gt; is used to create it.&lt;/p&gt;

&lt;p&gt;In the end, &lt;code&gt;jq&lt;/code&gt; is used to extract &lt;code&gt;auth_token&lt;/code&gt; into the file or to display the error message in case authentication was not successful. &lt;/p&gt;

&lt;p&gt;It is probably easier just to use vendor provided tools or libraries, but sometimes having a simple shell script is just good enough. &lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft Graph
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;replace_with_client_id&amp;gt;'&lt;/span&gt;
&lt;span class="nv"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;replace_with_tenant_id&amp;gt;'&lt;/span&gt;
&lt;span class="nv"&gt;cert_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'my.crt'&lt;/span&gt; &lt;span class="c"&gt;#certificate (used for fingerprint)&lt;/span&gt;
&lt;span class="nv"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'my.key'&lt;/span&gt;  &lt;span class="c"&gt;#certificate private key (for signing)&lt;/span&gt;
&lt;span class="nv"&gt;cert_hash&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl x509 &lt;span class="nt"&gt;-fingerprint&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="nv"&gt;$cert_file&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2 | xxd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0| &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;jwt_header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;alg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;RS256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;typ&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;JWT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;x5t&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$cert_hash&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;part_aud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;aud&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://login.microsoftonline.com/&lt;/span&gt;&lt;span class="nv"&gt;$tenant_id&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/v2.0/token&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_nbf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;nbf&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_exp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;exp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_jti&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;jti&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/sys/kernel/random/uuid&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_iss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iss&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$client_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_sub&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$client_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;jwt_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="nv"&gt;$part_aud&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_exp&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_iss&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_jti&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_nbf&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_sub&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="nv"&gt;token_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$jwt_header&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$jwt_payload&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$token_data&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-sign&lt;/span&gt; &lt;span class="nv"&gt;$key_file&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;assertion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$token_data&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$signature&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-Ssl&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://login.microsoftonline.com/&lt;/span&gt;&lt;span class="nv"&gt;$tenant_id&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/v2.0/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"client_id=&lt;/span&gt;&lt;span class="nv"&gt;$client_id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'scope=https://graph.microsoft.com/.default'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"client_assertion=&lt;/span&gt;&lt;span class="nv"&gt;$assertion&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=client_credentials'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | jq &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="s1"&gt;'.access_token'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;access_token
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.error + " error:\n" + .error_description'&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate"&gt;https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials"&gt;https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Google APIs
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;client_email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'client@your-project-name.iam.gserviceaccount.com'&lt;/span&gt;
&lt;span class="nv"&gt;subject_email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'subject@example.com'&lt;/span&gt; &lt;span class="c"&gt;#user that will be impersonated&lt;/span&gt;
&lt;span class="nv"&gt;scopes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://www.googleapis.com/auth/&amp;lt;scope1&amp;gt; https://www.googleapis.com/auth/&amp;lt;scope2&amp;gt;'&lt;/span&gt;
&lt;span class="nv"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'my.key'&lt;/span&gt; &lt;span class="c"&gt;#certificate private key (for signing)&lt;/span&gt;

&lt;span class="nv"&gt;jwt_header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;alg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;RS256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;typ&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;JWT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;part_iss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iss&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$client_email&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_sub&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$subject_email&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;scope&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$scopes&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_aud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;aud&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://oauth2.googleapis.com/token&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_exp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;exp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;part_iat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iat&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;jwt_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="nv"&gt;$part_iss&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_sub&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_scope&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_aud&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_exp&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$part_iat&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="nv"&gt;token_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$jwt_header&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$jwt_payload&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$token_data&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-sign&lt;/span&gt; &lt;span class="nv"&gt;$key_file&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'/+'&lt;/span&gt; &lt;span class="s1"&gt;'_-'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;assertion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$token_data&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$signature&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-Ssl&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://oauth2.googleapis.com/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"assertion=&lt;/span&gt;&lt;span class="nv"&gt;$assertion&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | jq &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="s1"&gt;'.access_token'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;access_token
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.error + " error:\n" + .error_description'&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/identity/protocols/oauth2/service-account#httprest"&gt;https://developers.google.com/identity/protocols/oauth2/service-account#httprest&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>oauth2</category>
      <category>bash</category>
      <category>microsoftgraph</category>
      <category>googleapi</category>
    </item>
    <item>
      <title>WSL2 Containers</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Fri, 07 Aug 2020 16:05:55 +0000</pubDate>
      <link>https://dev.to/milolav/wsl2-containers-45bp</link>
      <guid>https://dev.to/milolav/wsl2-containers-45bp</guid>
      <description>&lt;p&gt;Because, why not?&lt;/p&gt;

&lt;p&gt;Playing around with WSL2, I figured it could be used to make one-off build containers. In a similar way I usually use docker.&lt;/p&gt;

&lt;p&gt;For example, building TDLib for Telegram Messenger.&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;run.cmd&lt;/code&gt; is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@echo off
wsl --import alpine-build .\VM alpine-minirootfs-3.12.0-x86_64.tar.gz
wsl -d alpine-build /bin/sh /mnt/e/Temp/TDLib/build.sh
wsl --unregister alpine-build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install alpine distribution by running &lt;code&gt;wsl --import&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute the build script&lt;/li&gt;
&lt;li&gt;Unregister the distribution&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Build script, &lt;code&gt;build.sh&lt;/code&gt; , is made using TDLib &lt;a href="https://tdlib.github.io/td/build.html"&gt;Build instrunction generator&lt;/a&gt;, with the addition of the &lt;code&gt;OUT_DIR&lt;/code&gt; variable just to make it easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/e/Temp/TDLib/lib

&lt;span class="nb"&gt;cd&lt;/span&gt; ~
apk update
apk upgrade
apk add &lt;span class="nt"&gt;--update&lt;/span&gt; alpine-sdk linux-headers git zlib-dev openssl-dev gperf php php-ctype cmake
git clone https://github.com/tdlib/td.git
&lt;span class="nb"&gt;cd &lt;/span&gt;td
git checkout v1.6.0
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; build
&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="nb"&gt;cd &lt;/span&gt;build
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CXXFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
cmake &lt;span class="nt"&gt;-DCMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Release &lt;span class="nt"&gt;-DCMAKE_INSTALL_PREFIX&lt;/span&gt;:PATH&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OUT_DIR&lt;/span&gt; ..
cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="nb"&gt;install
cd&lt;/span&gt; ..
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;$OUT_DIR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running it and it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp\TDLib&amp;gt; .\run.cmd
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz
v3.12.0-214-g4076381e49 [http://dl-cdn.alpinelinux.org/alpine/v3.12/main]
v3.12.0-213-gfedcac4c0f [http://dl-cdn.alpinelinux.org/alpine/v3.12/community]
OK: 12749 distinct packages available
(1/6) Upgrading musl (1.1.24-r8 -&amp;gt; 1.1.24-r9)
&amp;lt;snip&amp;gt;
-- The CXX compiler identification is GNU 9.3.0
-- The C compiler identification is GNU 9.3.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ - works
&amp;lt;snip&amp;gt;
-- Configuring done
-- Generating done
-- Build files have been written to: /root/td/build
Scanning dependencies of target tdsqlite
[  0%] Building C object sqlite/CMakeFiles/tdsqlite.dir/sqlite/sqlite3.c.o
[  0%] Linking C static library libtdsqlite.a
[  0%] Built target tdsqlite
&amp;lt;snip&amp;gt;
Scanning dependencies of target bench_queue
[100%] Building CXX object benchmark/CMakeFiles/bench_queue.dir/bench_queue.cpp.o
[100%] Linking CXX executable bench_queue
[100%] Built target bench_queue
Install the project...
-- Install configuration: "Release"
-- Installing: /mnt/e/Temp/TDLib/lib/lib/libtdjson.so.1.6.0
-- Installing: /mnt/e/Temp/TDLib/lib/lib/libtdjson.so
-- Installing: /mnt/e/Temp/TDLib/lib/lib/libtdjson_static.a
-- Installing: /mnt/e/Temp/TDLib/lib/lib/libtdjson_private.a
&amp;lt;snip&amp;gt;
total 0
drwxrwxrwx    1 root     root           512 Aug  7 14:03 include
drwxrwxrwx    1 root     root           512 Aug  7 14:03 lib
Unregistering...
PS E:\Temp\TDLib&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Not as versatile as &lt;code&gt;docker run&lt;/code&gt; but does the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using differencing disks
&lt;/h2&gt;

&lt;p&gt;Using differencing disks, I can have all build tools installed in the base vhdx and spawn "build container"  just for a single build.&lt;/p&gt;

&lt;p&gt;For example, based on above, I've created base&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --import Alpine-BuildBase E:\WSL2\Alpine-BuildBase E:\WSL2\alpine-minirootfs-3.12.0-x86_64.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And installed everything I need&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;apk update
apk upgrade
apk add &lt;span class="nt"&gt;--update&lt;/span&gt; alpine-sdk linux-headers git zlib-dev openssl-dev gperf php php-ctype cmake
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now my &lt;code&gt;build.sh&lt;/code&gt; omits tools installation and looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/e/Temp/TDLib/lib

&lt;span class="nb"&gt;cd&lt;/span&gt; ~
git clone https://github.com/tdlib/td.git
&lt;span class="nb"&gt;cd &lt;/span&gt;td
git checkout v1.6.0
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; build
&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="nb"&gt;cd &lt;/span&gt;build
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CXXFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
cmake &lt;span class="nt"&gt;-DCMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Release &lt;span class="nt"&gt;-DCMAKE_INSTALL_PREFIX&lt;/span&gt;:PATH&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OUT_DIR&lt;/span&gt; ..
cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="nb"&gt;install
cd&lt;/span&gt; ..
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;$OUT_DIR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the final part, &lt;code&gt;run.ps1&lt;/code&gt; PowerShell script that will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create differencing vhdx&lt;/li&gt;
&lt;li&gt;Make a registry key for the linux distribution "container"&lt;/li&gt;
&lt;li&gt;Execute the build script&lt;/li&gt;
&lt;li&gt;Unregister the distribution
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Must be run as an Administrator&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;!&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;Security.Principal.&lt;/span&gt;&lt;span class="kt"&gt;WindowsPrincipal&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="no"&gt;Security.Principal.&lt;/span&gt;&lt;span class="kt"&gt;WindowsIdentity&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nf"&gt;GetCurrent&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsInRole&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;Security.Principal.&lt;/span&gt;&lt;span class="kt"&gt;WindowsBuiltInRole&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nx"&gt;Administrator&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="nf"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PowerShell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Verb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RunAs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-NoProfile -ExecutionPolicy Bypass -Command &lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="s2"&gt;cd '&lt;/span&gt;&lt;span class="nv"&gt;$pwd&lt;/span&gt;&lt;span class="s2"&gt;'; &amp;amp; '&lt;/span&gt;&lt;span class="bp"&gt;$PSCommandPath&lt;/span&gt;&lt;span class="s2"&gt;';&lt;/span&gt;&lt;span class="se"&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="kr"&gt;exit&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;$DISTRO_NAME&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;"alpine-build"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Create a new differencing VHD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;New-Item&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="nx"&gt;VM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&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="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-VHD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ParentPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;E:\WSL2\Alpine-BuildBase\ext4.vhdx&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="nx"&gt;\VM\ext4.vhdx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Differencing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-BlockSizeBytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1048576&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Add a registry key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$guid&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="nf"&gt;New-Guid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$RegKey&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;"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\"&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;$guid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;New-Item&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="nv"&gt;$RegKey&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;BasePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;String&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="bp"&gt;$PWD&lt;/span&gt;&lt;span class="nx"&gt;\VM&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;DefaultUid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DWord&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="nx"&gt;0&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;DistributionName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;String&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;$DISTRO_NAME&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;Flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DWord&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="nx"&gt;15&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DWord&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="nx"&gt;1&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;New-ItemProperty&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="nv"&gt;$RegKey&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;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PropertyType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DWord&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="nx"&gt;2&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Run the build script&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DISTRO_NAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/bin/sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/mnt/e/Temp/TDLib/build.sh&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Unregister the distribution&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--unregister&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DISTRO_NAME&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Pause&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;Read-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Prompt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Press Enter key to continue"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Just testing
&lt;/h2&gt;

&lt;p&gt;To make sure above script works as expected instead of running the build script (line 25 above) I can swap it with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;wsl&lt;/span&gt; &lt;span class="na"&gt;-d &lt;/span&gt;$DISTRO_NAME &lt;span class="na"&gt;/bin/sh -c &lt;/span&gt;&lt;span class="s2"&gt;"date &amp;amp;&amp;amp; cat /etc/*-release"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Result is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ComputerName            : PC123
Path                    : E:\Temp\TDLib\VM\ext4.vhdx
VhdFormat               : VHDX
VhdType                 : Differencing
FileSize                : 6291456
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576
ParentPath              : E:\WSL2\Alpine-BuildBase\ext4.vhdx
DiskIdentifier          : A96ADE57-4838-48EA-97A9-DE02790ADCB4
FragmentationPercentage :
Alignment               : 1
Attached                : False
DiskNumber              :
IsPMEMCompatible        : False
AddressAbstractionType  : None
Number                  :

Property      : {}
PSPath        : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{4
                e9ced54-7d28-48d2-92de-1df93ddc55a9}
PSParentPath  : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss
PSChildName   : {4e9ced54-7d28-48d2-92de-1df93ddc55a9}
PSDrive       : HKCU
PSProvider    : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount   : 0
View          : Default
Handle        : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount    : 0
Name          : HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{4e9ced54-7d28-48d2-92de-1df93ddc55a9}

Fri Aug  7 15:27:17 UTC 2020
3.12.0
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
Unregistering...
Press Enter key to continue:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I'm not sure if this has a practical value. Maybe in scenarios where it is easier to have a single file virtual disk rather than a docker container stored in some registry. But it was an interesting concept to explore. Let me know if you find it useful.&lt;/p&gt;

</description>
      <category>wsl</category>
    </item>
    <item>
      <title>Manually installing WSL2 distributions</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Fri, 07 Aug 2020 15:55:29 +0000</pubDate>
      <link>https://dev.to/milolav/manually-installing-wsl2-distributions-41b4</link>
      <guid>https://dev.to/milolav/manually-installing-wsl2-distributions-41b4</guid>
      <description>&lt;p&gt;There are a few different ways of installing Linux distributions for WSL. The easiest, using Microsoft Store, or using tools like &lt;a href="https://github.com/DDoSolitary/LxRunOffline"&gt;LxRunOffline&lt;/a&gt; or &lt;a href="https://github.com/yuk7/wsldl"&gt;wsldl&lt;/a&gt;. But distribution can be installed manually as well.&lt;/p&gt;

&lt;p&gt;WSL command line allows to export distribution to a tar file, or to import from a tar file as a new distribution.&lt;/p&gt;

&lt;p&gt;Let's say I have Ubuntu installed from Microsoft Store, if I list them from a command line I will get something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --list --all -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And if I export it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --export Ubuntu-20.04 E:\WSL2\ubuntu.tar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I will get a tar file containing a root filesystem of my Ubuntu distribution. And I can import that as a new distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --import Ubuntu-Copy E:\WSL2\Ubuntu-Copy E:\WSL2\ubuntu.tar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will create a folder &lt;code&gt;Ubuntu-Copy&lt;/code&gt; folder in the &lt;code&gt;E:\WSL2&lt;/code&gt; and the &lt;code&gt;ext4.vhdx&lt;/code&gt; file in that folder. Virtual hard disk is an ext4 partitioned disk containing the contents of the tar file. Listing distributions now shows two of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --list --all -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         2
  Ubuntu-Copy     Stopped         2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If I start the second distribution, I will be logged in as a root account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl -d Ubuntu-Copy
root@PC123:/mnt/e/Temp#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, so if import works for the exported root filesystem, will it work for any root filesystem? And the answer is Yes!&lt;/p&gt;

&lt;p&gt;For example, if I download &lt;code&gt;alpine-minirootfs-3.12.0-x86_64.tar.gz&lt;/code&gt; from &lt;a href="https://alpinelinux.org/downloads/"&gt;https://alpinelinux.org/downloads/&lt;/a&gt;  I can import it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --import Alpine-3.12 E:\WSL2\Alpine-3.12 E:\WSL2\alpine-minirootfs-3.12.0-x86_64.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It even works with tar.gz files, not just .tar files. In fact, Alpine distribution available in Microsoft Store does exactly that. Downloads this file and installs it. Source for the launcher is available here: &lt;a href="https://github.com/agowa338/WSL-DistroLauncher-Alpine"&gt;https://github.com/agowa338/WSL-DistroLauncher-Alpine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to the reference implementation of the launcher (&lt;a href="https://github.com/microsoft/WSL-DistroLauncher"&gt;WSL-DistroLauncher&lt;/a&gt;) it allows user creation on start-up and it has some additional fixes. See the &lt;a href="https://github.com/agowa338/WSL-DistroLauncher-Alpine/blob/master/DistroLauncher/DistroSpecial.h"&gt;DistroSpecial.h&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;After distribution is complete I can manually create user, the same way launcher does&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl -d Alpine-3.12 /usr/sbin/adduser -g '' -D user1
Changing password for user1
New password:
Retype password:
passwd: password for user1 changed by root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then logging in like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl -d Alpine-3.12 -u user1
PC123:/mnt/e/Temp$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In a nutshell this is it. A new distribution is manually installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important thing to note&lt;/strong&gt;:&lt;br&gt;
By unregistering the distribution like &lt;code&gt;wsl --unregister Ubuntu-20.04&lt;/code&gt; it will &lt;strong&gt;delete the associated &lt;code&gt;ext4.vhdx&lt;/code&gt; file.&lt;/strong&gt; Make sure to backup any important data form the virtual hard disk before unregistering.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start-up scripts
&lt;/h2&gt;

&lt;p&gt;Instead manually creating users and setting everything up, I can write all commands in a text file and execute them.&lt;/p&gt;

&lt;p&gt;For example, a dummy &lt;code&gt;init&lt;/code&gt; file that displays current date and release name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;date
cat /etc/*-release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Start like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E:\Temp&amp;gt; wsl -d Debian-10-Slim &amp;lt; init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the result is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fri Aug  6 23:08:29 BST 2020
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or run that file directly as a shell script from a know location&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl -d Alpine-3.12 /bin/sh /mnt/e/Temp/init

Fri Aug  6 22:17:05 UTC 2020
3.12.0
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Registry keys
&lt;/h2&gt;

&lt;p&gt;When distribution is installed its settings are stored in the registry &lt;code&gt;HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{GUID}&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Guid is, as far as I can tell, randomly generated for every distribution installation&lt;/p&gt;

&lt;p&gt;For example, official installation of Ubuntu creates similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{2506e37c-8479-4433-9ea8-7549090aa5bf}]
"State"=dword:00000001
"DistributionName"="Ubuntu-20.04"
"Version"=dword:00000002
"BasePath"="C:\\Users\\User\\AppData\\Local\\Packages\\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\\LocalState"
"Flags"=dword:0000000f
"DefaultUid"=dword:000003e8
"PackageFamilyName"="CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc"
"KernelCommandLine"="BOOT_IMAGE=/kernel init=/init"
"DefaultEnvironment"=hex(7):&amp;lt;snipped&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which means that virtual hard disk is stored in the &lt;code&gt;C:\Users\User\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState&lt;/code&gt; folder and default Uid is 1000 (0x3e8).&lt;/p&gt;

&lt;p&gt;Looking at the Alpine distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{b9a51057-c168-4798-a89e-483c97278f3c}]
"State"=dword:00000001
"DistributionName"="Alpine-3.12"
"Version"=dword:00000002
"BasePath"="\\\\?\\E:\\WSL2\\Alpine-3.12"
"Flags"=dword:0000000f
"DefaultUid"=dword:00000000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;it omits some settings, but &lt;code&gt;BasePath&lt;/code&gt; points to a folder containing &lt;code&gt;ext4.vhdx&lt;/code&gt; file and &lt;code&gt;DefaultUid&lt;/code&gt; is the Uid of the user when starting the distribution without the &lt;code&gt;-u &amp;lt;user&amp;gt;&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Moving a distribution to a different location requires just to move the &lt;code&gt;ext4.vhdx&lt;/code&gt; to a new folder and update the &lt;code&gt;BasePath&lt;/code&gt; value. Similarly, changing a default user requires an update to the &lt;code&gt;DefaultUid&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;To "restore" a missing distribution, copy a registry key, update the &lt;code&gt;DistributionName&lt;/code&gt;,  &lt;code&gt;BasePath&lt;/code&gt;, and &lt;code&gt;DefaultUid&lt;/code&gt; values, and it is ready to go. Or import an empty tar file (created like &lt;code&gt;tar -zvcf empty.tar.gz -T /dev/null&lt;/code&gt;) and swap out the resulting empty ext4.vhdx with the real one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other distributions
&lt;/h2&gt;

&lt;p&gt;Following the examples from the  &lt;a href="https://github.com/yuk7/wsldl"&gt;wsldl&lt;/a&gt; project, a simple way of installing different linux distributions is to take root filesystem used to make docker images. For example &lt;a href="https://github.com/debuerreotype/docker-debian-artifacts/blob/dist-amd64/buster/slim/Dockerfile"&gt;Dockerfile for Debian Buster Slim&lt;/a&gt; says &lt;code&gt;ADD rootfs.tar.xz /&lt;/code&gt;. I can download the &lt;a href="https://github.com/debuerreotype/docker-debian-artifacts/blob/dist-amd64/buster/slim/rootfs.tar.xz"&gt;rootfs.tar.xz&lt;/a&gt;, unpack it since WSL doesn't recognize tar.xz file format, import it and run it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; wsl --import Debian-10-Slim E:\WSL2\Debian-10-Slim E:\WSL2\debian-buster-slim-20200803-rootfs.tar
PS E:\Temp&amp;gt; wsl -d Debian-10-Slim
root@PC123:/mnt/e/Temp#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Official &lt;code&gt;install.tar.gz&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When installing Ubuntu or Debian Linux distribution from the Microsoft Store root filesystem is downloaded as a part of Store App installation. This means that initial filesystem tar, &lt;code&gt;install.tar.gz&lt;/code&gt; can be found in the &lt;code&gt;C:\Program Files\WindowsApps \CanonicalGroupLimited.Ubuntu20.04onWindows_2004.2020.424.0_x64__79rhkp1fndgsc\&lt;/code&gt;  folder, for the Ubuntu 20.04 installation. &lt;/p&gt;

&lt;p&gt;For a different version, folder name in the &lt;code&gt;C:\Program Files\WindowsApps&lt;/code&gt; will be different, but it should contain &lt;code&gt;install.tar.gz&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;And that file can be imported as a new distribution in the same way as mentioned above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Differencing disks
&lt;/h2&gt;

&lt;p&gt;Since WSL2 is using vhdx disks, I was wondering can I use differencing disks. And the answer is again Yes.&lt;/p&gt;

&lt;p&gt;Having 5 instances of Ubuntu will take about 5GB of space immediately. Base vhdx is about 1GB. But, by leveraging differencing disks, I can have one base disk, and 5 smaller, differencing disks.&lt;/p&gt;

&lt;p&gt;First stop all distributions with &lt;code&gt;wsl --shutdown&lt;/code&gt;, then move existing &lt;code&gt;ext4.vhdx&lt;/code&gt; to a new folder (one level up in my case), and create a differencing disk with the &lt;code&gt;ext4.vhdx&lt;/code&gt; name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\WSL2\Ubuntu-20.04&amp;gt; New-VHD -ParentPath ..\base.vhdx -Path ext4.vhdx -Differencing -BlockSizeBytes 1048576
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When creating virtual hard disks for linux, &lt;strong&gt;make sure that you use 1MB block size&lt;/strong&gt;. Default block size of 32MB will result in fairly large vhdx file once it starts to get filled.&lt;/p&gt;

&lt;p&gt;Only article where I found this recommendation is &lt;a href="https://www.altaro.com/hyper-v/understanding-working-vhdx-files/"&gt;Understanding and Working with VHD(X) Files&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For Linux systems, there is a soft recommendation to use a 1 megabyte block size&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By examining &lt;code&gt;ext4.vhdx&lt;/code&gt; created by the installation, details also show 1MB block size&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\WSL2\Ubuntu-Copy&amp;gt; Get-VHD .\ext4.vhdx | Format-List

ComputerName            : PC123
Path                    : E:\WSL2\Ubuntu-Copy\ext4.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 1164967936
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576
ParentPath              :
DiskIdentifier          : A96ADE57-4838-48EA-97A9-DE02790ADCB4
FragmentationPercentage : 53
Alignment               : 1
Attached                : False
DiskNumber              :
IsPMEMCompatible        : False
AddressAbstractionType  : None
Number                  :
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  BlockSize effect on a dynamic disk file size
&lt;/h3&gt;

&lt;p&gt;A little digression. Comparing a file size for vhdx with 1MB block size vs default 32MB block size.&lt;/p&gt;

&lt;p&gt;I've created two vhdx files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; New-VHD -Path .\256_default.vhdx -SizeBytes 256GB

Path                    : E:\Temp\256_default.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 4194304
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 33554432
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; New-VHD -Path .\256_1mb.vhdx -SizeBytes 256GB -BlockSizeBytes 1MB

Path                    : E:\Temp\256_1mb.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 6291456
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Initial sizes were 4MB and 6MB.&lt;/p&gt;

&lt;p&gt;Attached them to a Hyper-V VM, booted the &lt;code&gt;alpine-virt-3.12.0-x86_64.iso&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Then I ran  &lt;code&gt;mkfs.ext4 /dev/sda&lt;/code&gt; and  &lt;code&gt;mkfs.ext4 /dev/sdb&lt;/code&gt; and shut down the VM.&lt;/p&gt;

&lt;p&gt;Resulting files are&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; Get-VHD .\256_default.vhdx | Format-List

Path                    : E:\Temp\256_default.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 4869586944
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 33554432
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS E:\Temp&amp;gt; Get-VHD .\256_1mb.vhdx | Format-List

Path                    : E:\Temp\256_1mb.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 162529280
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, after making the ext4 filesystem on a 256GB dynamic disk, the one with a 32MB block size grew to &lt;strong&gt;4,53GB&lt;/strong&gt; while while the one with the 1MB block size grew to just 155MB.&lt;/p&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;Anyway, WSL2 seems interesting enough 😁&lt;/p&gt;

</description>
      <category>wsl</category>
    </item>
    <item>
      <title>Socket activation of containers using Traefik's ForwardAuth
</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Sat, 25 Jul 2020 17:25:09 +0000</pubDate>
      <link>https://dev.to/milolav/socket-activation-of-containers-using-traefik-s-forwardauth-4hmo</link>
      <guid>https://dev.to/milolav/socket-activation-of-containers-using-traefik-s-forwardauth-4hmo</guid>
      <description>&lt;p&gt;This is just an idea of mixing &amp;amp; matching a few services to get something like socket activation for docker containers.&lt;/p&gt;

&lt;p&gt;Let's say there is a web hook which isn't triggered very often and it is created as a docker container. There is no point of running the container all the time just for an occasional calls to a web hook. But there is a problem how to start a container when needed, when a request is sent for that hook. Of course I could have something like OpenFaaS but that seems like a overkill for an intermittent process. In addition, OpenFaaS doesn't support mounting volumes by design so if I want to receive a file, confirm that it is received, process it in the background and store it somewhere on a filesystem, it requires an additional effort.&lt;/p&gt;

&lt;p&gt;I'm already using &lt;a href="https://traefik.io"&gt;Traefik&lt;/a&gt; as a reverse proxy and &lt;a href="https://github.com/adnanh/webhook"&gt;webhook&lt;/a&gt; for some scenarios and I wanted to see if I could use that to achieve something similar to socket activation.&lt;/p&gt;

&lt;p&gt;Traefik doesn't have "execute this shell script" middleware, but it does have &lt;a href="https://docs.traefik.io/middlewares/forwardauth/"&gt;ForwardAuth&lt;/a&gt; middleware. It sends the request to the AuthServer and if the response code is 2XX access is granted ad original request is performed.&lt;/p&gt;

&lt;p&gt;So my idea was: &lt;em&gt;If I use webhook as an "AuthServer" with a hook being a shell script that starts a container, could this work?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As it turns out, &lt;strong&gt;it does work&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Instead of an actual container doing some work, this example will just be starting simple nginx container, and stopping it after 20 seconds.&lt;/p&gt;

&lt;p&gt;In a proper setup, instead of a nginx there would be a real container doing real processing and after processing is complete it would exit gracefully. Instead of blindly running a container, and stopping it after 20 seconds, there would be check in place whether the container is already running etc. Pretty much everything that OpenFaaS Gateway module does.  But this is just a general idea.&lt;/p&gt;

&lt;p&gt;First I have a docker container which has webhook and docker-cli installed, built as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;        golang:alpine3.11 AS build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt;     /go/src/github.com/adnanh/webhook&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt;         WEBHOOK_VERSION 2.7.0&lt;/span&gt;
&lt;span class="k"&gt;RUN         &lt;/span&gt;apk add &lt;span class="nt"&gt;--update&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; build-deps curl libc-dev gcc libgcc &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://github.com/adnanh/webhook/archive/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WEBHOOK_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;              | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /go/src/github.com/adnanh/webhook &lt;span class="nt"&gt;--strip&lt;/span&gt; 1 &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; go build &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/webhook

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;        alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt;        --from=build /usr/local/bin/webhook /usr/local/bin/&lt;/span&gt;
&lt;span class="k"&gt;RUN         &lt;/span&gt;apk update &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; docker-cli
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt;     /etc/webhook&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt;  ["/usr/local/bin/webhook"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That container has configuration mounted as &lt;code&gt;/etc/webhook/hooks.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"start-nginx1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"execute-command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/etc/webhook/scripts/nginx1"&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;A shell script that will be executed, which starts a container (mounted as &lt;code&gt;/etc/webhook/scripts/nginx1&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; nginx1 &lt;span class="nt"&gt;--network&lt;/span&gt; skynet nginx:alpine
&lt;span class="nb"&gt;sleep &lt;/span&gt;5
&lt;span class="nb"&gt;nohup&lt;/span&gt; /etc/webhook/scripts/nginx1-remove &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp; /dev/null &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And a shell script that will stop the container after 20 seconds&lt;br&gt;
(mounted as &lt;code&gt;/etc/webhook/scripts/nginx1-remove&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;20 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker stop nginx1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;nginx1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With that in place, let's call this container &lt;code&gt;activator&lt;/code&gt; and start it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;activator &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-v&lt;/span&gt; /srv/activator:/etc/webhook &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock:ro &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--network&lt;/span&gt; skynet &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt;/activator &lt;span class="nt"&gt;-hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/webhook/hooks.json &lt;span class="nt"&gt;-verbose&lt;/span&gt; &lt;span class="nt"&gt;-hotreload&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For Traefik, configuration (dynamic part) is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[http.routers]&lt;/span&gt;
  &lt;span class="nn"&gt;[http.routers.nginx1-http]&lt;/span&gt;
    &lt;span class="py"&gt;entryPoints&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["web"]&lt;/span&gt;
    &lt;span class="py"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Host(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;nginx1.myservers.local&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;
    &lt;span class="py"&gt;middlewares&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["auth-run"]&lt;/span&gt;
    &lt;span class="py"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nginx1"&lt;/span&gt;

&lt;span class="nn"&gt;[http.middlewares]&lt;/span&gt;
  &lt;span class="nn"&gt;[http.middlewares.auth-run.forwardAuth]&lt;/span&gt;
    &lt;span class="py"&gt;address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://activator:9000/hooks/nginx1"&lt;/span&gt;
&lt;span class="nn"&gt;[[http.services.nginx1.loadBalancer.servers]]&lt;/span&gt;
  &lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://nginx1/"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The execution
&lt;/h2&gt;

&lt;p&gt;With all of the above set, and given that dns entries are correct and all containers are on the same network once the &lt;a href="http://nginx1.myservers.local"&gt;http://nginx1.myservers.local&lt;/a&gt; is opened in a browser, the following will happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser will send a request to the Traefik&lt;/li&gt;
&lt;li&gt;Traefik will, as a part of the authorization, call the nginx1 hook&lt;/li&gt;
&lt;li&gt;Webhook will run a shell script&lt;/li&gt;
&lt;li&gt;Shell script will start docker container and start remove script detached&lt;/li&gt;
&lt;li&gt;If starting a shell script was successful, webhook will return code 200 to Traefik&lt;/li&gt;
&lt;li&gt;Traefik will accept that as a authorization confirmation and pass the request to the, now running, nginx1 container&lt;/li&gt;
&lt;li&gt;Nginx will respond with a welcome page&lt;/li&gt;
&lt;li&gt;Welcome page (from nginx) will be displayed in a browser&lt;/li&gt;
&lt;li&gt;After 20 seconds, nginx1 container will be removed&lt;/li&gt;
&lt;li&gt;It works!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a scenario where something wrong happens with a script (for example, a subsequent request within the 20sec period will fail to start a container with the same name while it is already running), webhook will not return 200, Traefik's authentication sequence will fail and  browser will get unauthorized error.&lt;/p&gt;

&lt;p&gt;In a way this is exploiting ForwardAuth middleware for something totally different, but it does the trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  The application
&lt;/h2&gt;

&lt;p&gt;Where I see this idea fit is for intermittent web hooks that do some processing in the background. For example, when a web hook is triggered, response is sent right away (confirmation that data is received), while the processing continues in the background, and once complete, container can shut down.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>traefik</category>
      <category>webhook</category>
    </item>
    <item>
      <title>Reload cron jobs in an Alpine linux container
</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Sat, 25 Jul 2020 14:42:44 +0000</pubDate>
      <link>https://dev.to/milolav/reload-cron-jobs-in-an-alpine-linux-container-486b</link>
      <guid>https://dev.to/milolav/reload-cron-jobs-in-an-alpine-linux-container-486b</guid>
      <description>&lt;p&gt;TL;DR&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After updating &lt;code&gt;/etc/crontabs/root&lt;/code&gt; , putting an empty &lt;code&gt;cron.update&lt;/code&gt; file in the &lt;code&gt;/etc/crontabs&lt;/code&gt; will update directory's last modification, cron daemon will reload jobs from the files in the  &lt;code&gt;/etc/crontabs&lt;/code&gt; and &lt;code&gt;cron.update&lt;/code&gt; file will be deleted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A bit more about why and how
&lt;/h3&gt;

&lt;p&gt;In a situation where container's crontabs directory (&lt;code&gt;/etc/crontabs&lt;/code&gt;) is mounted from a host, just editing file that container sees as a &lt;code&gt;/etc/crontabs/root&lt;/code&gt; file outside of a container will not trigger a cron daemon to reload jobs.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;a href="https://git.busybox.net/busybox/tree/miscutils/crond.c"&gt;busybox source code&lt;/a&gt;, there is a comment saying &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The file 'cron.update' is checked to determine new cron jobs.  The directory is rescanned once an hour to deal with any screwups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, the code is checking if the &lt;code&gt;/etc/crontabs&lt;/code&gt; directory's last modification time changed which triggers a job update. To update last modification time of a directory, something should be added, removed or renamed inside that directory (excluding subdirectories).  For example, adding a random file put into the &lt;code&gt;/etc/crontabs&lt;/code&gt; will update last modification time which will trigger reloading jobs in the next cycle (the next minute). This is good, since container doesn't have to be restarted for new jobs to take effect. The downside is a lack of feedback that this really happened.&lt;/p&gt;

&lt;p&gt;So, instead of a random file, a file named &lt;code&gt;cron.update&lt;/code&gt; can be used to get a confirmation that jobs have been reloaded. File is used by crontab to notify cron daemon that a cron file has been updated and a cron daemon will delete &lt;code&gt;cron.update&lt;/code&gt; once it reloads the jobs, meaning that if the file is gone, jobs are reloaded. Crontab writes the changed file name into the &lt;code&gt;cron.update&lt;/code&gt;  file, but since adding a file updates directory's last modification time, an empty file will do the trick.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>alpine</category>
      <category>cron</category>
    </item>
    <item>
      <title>Quickly create a self signed certificate for a custom domain</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Mon, 13 Apr 2020 20:03:55 +0000</pubDate>
      <link>https://dev.to/milolav/quickly-create-a-self-signed-certificate-for-a-custom-domain-3e7k</link>
      <guid>https://dev.to/milolav/quickly-create-a-self-signed-certificate-for-a-custom-domain-3e7k</guid>
      <description>&lt;p&gt;I often find myself needing a simple self signed certificate that is either for a wildcard domain or that cover multiple domain names. And then, even when I have openssl somewhere the questions are "do I have the config file?", "do I really need to fill in all those distinguished name values?", "where do I put additional domains", etc.&lt;/p&gt;

&lt;p&gt;So to make my life simpler I made a small batch script that takes domain names as arguments, creates configuration file and instructs openssl to create a self signed certificate for those domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example usage
&lt;/h2&gt;

&lt;p&gt;Requesting certificates like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E:\Temp&amp;gt; self-signed-cert mydomain.local *.mydomain.local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;will generate one certificate with 2 domain names (&lt;code&gt;mydomain.local&lt;/code&gt; and &lt;code&gt;*.mydomain.local&lt;/code&gt;) and will produce the following files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;mydomain.local.conf - configuration file for openssl&lt;/li&gt;
&lt;li&gt;mydomain.local.key - private key&lt;/li&gt;
&lt;li&gt;mydomain.local.crt - certificate&lt;/li&gt;
&lt;li&gt;mydomain.local.pfx - a pkcs#12 file with both certificate and private key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Content of the generated &lt;code&gt;mydomain.local.conf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[req]&lt;/span&gt;
&lt;span class="py"&gt;default_bits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2048&lt;/span&gt;
&lt;span class="py"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
&lt;span class="py"&gt;default_md&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sha256&lt;/span&gt;
&lt;span class="py"&gt;distinguished_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;req_distinguished_name&lt;/span&gt;
&lt;span class="py"&gt;x509_extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;v3_req&lt;/span&gt;

&lt;span class="nn"&gt;[req_distinguished_name]&lt;/span&gt;
&lt;span class="py"&gt;CN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mydomain.local&lt;/span&gt;

&lt;span class="nn"&gt;[v3_req]&lt;/span&gt;
&lt;span class="py"&gt;subjectAltName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@san&lt;/span&gt;

&lt;span class="nn"&gt;[san]&lt;/span&gt;
&lt;span class="py"&gt;DNS.1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mydomain.local&lt;/span&gt;
&lt;span class="py"&gt;DNS.2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;*.mydomain.local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the generated certificate looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            fc:55:a1:88:a5:68:67:0c
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = mydomain.local
        Validity
            Not Before: Apr 13 12:12:57 2020 GMT
            Not After : Apr 11 12:12:57 2030 GMT
        Subject: CN = mydomain.local
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:cb:d9:5c:bb:df:23:1e:69:5d:b2:45:06:d8:1f:
                    d9:d1:4a:37:07:ab:c7:86:77:e0:2f:88:36:dc:f4:
                                      &amp;lt;...&amp;gt;
                    7d:70:b6:23:aa:9a:bc:57:83:c9:f8:13:3f:9f:b4:
                    01:54:71
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:mydomain.local, DNS:*.mydomain.local
    Signature Algorithm: sha256WithRSAEncryption
         b0:7b:4c:2c:f6:5e:40:2b:9d:9d:71:b1:d3:f8:0b:44:84:50:
         d1:ea:b0:91:fb:61:7d:d8:f8:d9:ca:5d:c8:b2:bf:d4:5d:75:
                               &amp;lt;...&amp;gt;
         5c:73:66:d1:52:10:11:2b:68:85:da:8a:16:3d:82:4f:0c:c5:
         50:a5:85:81:4f:06:27:33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Batch file explained
&lt;/h2&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]==[]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;Usage&lt;/span&gt;: &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="se"&gt;^&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;domain_name&lt;/span&gt;&lt;span class="se"&gt;^&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;additional_domain&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;additional_domain&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; ...
  &lt;span class="k"&gt;exit&lt;/span&gt; &lt;span class="na"&gt;/b &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Just making sure that there is at least one argument&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]==[]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;addsan&lt;/span&gt;&lt;span class="o"&gt;=)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;addsan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Checking if it is a single domain name or multiple domain name&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="vm"&gt;%fn&lt;/span&gt;:&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[*&lt;/span&gt;.&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;_wildcard.&lt;span class="vm"&gt;%fn&lt;/span&gt;:&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Setting a file name for configuration and certificates. If the (first) domain name in the list is a wildcard domain, replace the asterisk character with "wildcard" word.  Otherwise just take the domain name. So requesting a &lt;code&gt;*.mydomain.local&lt;/code&gt; certificates will generate &lt;code&gt;wildcard.mydomain.local&lt;/code&gt; files, while requesting &lt;code&gt;web1.mydomain.local&lt;/code&gt;  certificate will generate &lt;code&gt;web1.mydomain.local&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%~1&lt;/code&gt; - removing surrounding quotes around first argument. E.g. if the first argument is &lt;code&gt;"something with spaces"&lt;/code&gt;, the &lt;code&gt;%~1&lt;/code&gt; will remove the quotes.&lt;br&gt;
&lt;code&gt;%fn:~0,2%&lt;/code&gt; - substring, first two characters of the &lt;code&gt;fn&lt;/code&gt; variable&lt;br&gt;
&lt;code&gt;%fn:~2%&lt;/code&gt; - substring, everything after 2nd character&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;req&lt;/span&gt;&lt;span class="o"&gt;]&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;default_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;no&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;default_md&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;distinguished_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;req_distinguished_name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;%addsan%&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;x509_extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;v3_req&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt;.&amp;gt;&amp;gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;req_distinguished_name&lt;/span&gt;&lt;span class="o"&gt;]&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;CN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt;.&amp;gt;&amp;gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Making the openssl configuration, named &lt;code&gt;%fn%.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;%addsan%&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="nl"&gt;:makecert&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Skip additional configuration if there is only one domain&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;v3_req&lt;/span&gt;&lt;span class="o"&gt;]&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;subjectAltName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; @san&amp;gt;&amp;gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt;.&amp;gt;&amp;gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;san&lt;/span&gt;&lt;span class="o"&gt;]&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="na"&gt;/a &lt;/span&gt;&lt;span class="kd"&gt;sanid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Additional configuration sections for multiple domain names.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nl"&gt;:sanloop&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="na"&gt;/a &lt;/span&gt;&lt;span class="kd"&gt;sanid&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;DNS&lt;/span&gt;.&lt;span class="nv"&gt;%sanid%&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf
&lt;span class="nb"&gt;shift&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]==[]&lt;/span&gt; &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="nl"&gt;:makecert&lt;/span&gt;
&lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="nl"&gt;:sanloop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Looping through the arguments. &lt;br&gt;
&lt;code&gt;set /a sanid+=1&lt;/code&gt; - increment counter so that each DNS entry has it's own number&lt;br&gt;
&lt;code&gt;shift&lt;/code&gt; - shift arguments so that %2 becomes %1, %3 becomes %2, etc&lt;br&gt;
&lt;code&gt;if [%~1]==[] goto :makecert&lt;/code&gt; - once there are no more arguments, break the loop&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nl"&gt;:makecert&lt;/span&gt;
&lt;span class="kd"&gt;openssl&lt;/span&gt; &lt;span class="kd"&gt;req&lt;/span&gt; &lt;span class="na"&gt;-new -x&lt;/span&gt;&lt;span class="m"&gt;509&lt;/span&gt; &lt;span class="na"&gt;-nodes -days &lt;/span&gt;&lt;span class="m"&gt;3650&lt;/span&gt; &lt;span class="na"&gt;-sha&lt;/span&gt;&lt;span class="m"&gt;256&lt;/span&gt; &lt;span class="na"&gt;-newkey &lt;/span&gt;&lt;span class="kd"&gt;rsa&lt;/span&gt;:4096 &lt;span class="na"&gt;-keyout &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.key &lt;span class="na"&gt;-out &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.crt &lt;span class="na"&gt;-config &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.conf

&lt;span class="kd"&gt;openssl&lt;/span&gt; &lt;span class="kd"&gt;pkcs12&lt;/span&gt; &lt;span class="na"&gt;-export -out &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.pfx &lt;span class="na"&gt;-inkey &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.key &lt;span class="na"&gt;-in &lt;/span&gt;&lt;span class="nv"&gt;%fn%&lt;/span&gt;.crt &lt;span class="na"&gt;-name &lt;/span&gt;&lt;span class="nv"&gt;%friendly%&lt;/span&gt; &lt;span class="na"&gt;-passout &lt;/span&gt;&lt;span class="kd"&gt;pass&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Finally, the openssl call. Frist create a new self signed certificate which is valid for 10 years, and then export it to pkcs#12  pfx file with an empty password.&lt;/p&gt;

&lt;p&gt;Make sure that openssl is somewhere in PATH.&lt;/p&gt;
&lt;h2&gt;
  
  
  self-signed-cert.cmd
&lt;/h2&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>openssl</category>
      <category>batch</category>
    </item>
    <item>
      <title>Simple Docker HTTP socket certificates</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Thu, 09 Apr 2020 21:00:29 +0000</pubDate>
      <link>https://dev.to/milolav/simple-docker-http-socket-certificates-cf7</link>
      <guid>https://dev.to/milolav/simple-docker-http-socket-certificates-cf7</guid>
      <description>&lt;p&gt;I have a couple of Docker servers and I wanted to be able to manage them remotely. Reading about it, adding &lt;code&gt;-H tcp://0.0.0.0:2375&lt;/code&gt; argument when starting docker daemon was simple enough solution but it also came with a warning that it is not secure.&lt;/p&gt;

&lt;p&gt;In addition, there is a &lt;a href="https://docs.docker.com/engine/security/https/"&gt;Protect the Docker daemon socket&lt;/a&gt; guide which covers how to expose http socket in a secure manner but it is also marked as an "Advanced topic" so I decided to make a simple shell script out of it.&lt;/p&gt;

&lt;p&gt;Tested on a clean Debian 10 after Docker installation. Full &lt;code&gt;docker-certs.sh&lt;/code&gt; is at the bottom of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-cert.sh [options]
OPTIONS:
  --all           Same as --ca --server --client --service combined, default behaviour
  --ca            Creates only self-signed Root CA
  --server        Creates a server certificate, requires CA to be created first
  --client        Creates a client certificate with a default name, requires CA to be created first
  --client=Name   Creates a client certificate with provided name, requires CA to be created first
  --service       Updates /lib/systemd/system/docker.service with socket listener and tls verification

Note: all files will be created in /etc/docker/certs dir
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Running it without any options like &lt;code&gt;sudo docker-cert.sh&lt;/code&gt; will execute default behaviour, same as with the &lt;code&gt;--all&lt;/code&gt; option. Script will do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if  &lt;code&gt;/etc/docker/certs&lt;/code&gt; directory exists, create if not&lt;/li&gt;
&lt;li&gt;Create a self-signed root CA with a common name &lt;code&gt;Docker ($FQDN) CA&lt;/code&gt;. FQDN is &lt;code&gt;hostname&lt;/code&gt; plus the first suffix from &lt;code&gt;/etc/resolv.conf&lt;/code&gt; search line, or just hostname if search line is not found.&lt;/li&gt;
&lt;li&gt;Create a server certificate with a common name  &lt;code&gt;Docker ($FQDN) Server&lt;/code&gt;, signed by the root CA&lt;/li&gt;
&lt;li&gt;Create a client certificate  with a common name  &lt;code&gt;Docker ($FQDN) Client&lt;/code&gt;, signed by the root CA&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;/lib/systemd/system/docker.service&lt;/code&gt; to append socket listener and certificates to the start command. Like the following:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-H tcp://0.0.0.0:2376
--tlsverify
--tlscacert=/etc/docker/certs/ca.pem
--tlscert=/etc/docker/certs/server.pem
--tlskey=/etc/docker/certs/server.key 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Reloading systemctl daemon, &lt;code&gt;systemctl daemon-reload&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restart docker service, &lt;code&gt;systemctl restart docker&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are checks in place so if a file that is to be created already exist, script will exit. In the same way if &lt;code&gt;docker.service&lt;/code&gt; contains &lt;code&gt;-H tcp://&lt;/code&gt; in the &lt;code&gt;ExecStart&lt;/code&gt; line, script will exit.&lt;/p&gt;
&lt;h3&gt;
  
  
  Additional clients
&lt;/h3&gt;

&lt;p&gt;Additional clients can be created afterwards by running &lt;code&gt;sudo docker-cert.sh --client=Another&lt;/code&gt; which will create &lt;code&gt;client_another.key&lt;/code&gt; and &lt;code&gt;client_another.pem&lt;/code&gt; files, and common name will be &lt;code&gt;Docker ($FQDN) Client Another&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Accessing remotely from Windows
&lt;/h2&gt;

&lt;p&gt;Once certificates are created and docker daemon is running, it is time to test connectivity from a client windows machine. Tutorial suggests&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--tlsverify&lt;/span&gt; &lt;span class="nt"&gt;--tlscacert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ca.pem &lt;span class="nt"&gt;--tlscert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cert.pem &lt;span class="nt"&gt;--tlskey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;key.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOST&lt;/span&gt;:2376
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;or setting environment variables like DOCKER_HOST or DOCKER_CERT_PATH for a directory where certificates are stored.&lt;/p&gt;

&lt;p&gt;First approach looks cumbersome to type every time, and second approach doesn't work for multiple servers with different certificates.&lt;/p&gt;
&lt;h3&gt;
  
  
  Batch file
&lt;/h3&gt;

&lt;p&gt;I opted to create a small batch file to interact with docker.&lt;/p&gt;

&lt;p&gt;Let's say that my docker server is named &lt;code&gt;dock1&lt;/code&gt;, domain suffix is &lt;code&gt;local&lt;/code&gt;. I've created certificates using the script so I have &lt;code&gt;Docker (dock1.local) Server&lt;/code&gt; certificate.&lt;/p&gt;

&lt;p&gt;After that I copied &lt;code&gt;ca.pem&lt;/code&gt;, &lt;code&gt;client.key&lt;/code&gt;, &lt;code&gt;client.pem&lt;/code&gt; from server to my windows machine into the &lt;code&gt;%USERPROFILE%\.docker\dock1&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Created a batch file named &lt;code&gt;dock1.bat&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DATADIR&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;USERPROFILE&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;\.docker\&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;~n0&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="nf"&gt;exist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\host.var"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\host.var"&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="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;~n0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--tlsverify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--tlscacert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\ca.pem"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--tlscert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\client.pem"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--tlskey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\client.key"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-H&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;2376&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;and put that batch file somewhere in the PATH.&lt;/p&gt;

&lt;p&gt;(broken into multiple lines for readability):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DATADIR&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;USERPROFILE&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;\.docker\&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;~n0&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="nf"&gt;exist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\host.var"&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="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\host.var"&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="nf"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;~n0&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="nf"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlsverify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlscacert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\ca.pem"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlscert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\client.pem"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlskey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%DATADIR%\client.key"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-H&lt;/span&gt;&lt;span class="o"&gt;=%&lt;/span&gt;&lt;span class="nf"&gt;DOCKERHOST&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;2376&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;So when I run &lt;code&gt;dock1 info&lt;/code&gt; from the command line that translates into  the following command (broken into multiple lines for readability):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nf"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlsverify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlscacert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"C:\Users\user\ca.pem"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlscert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"C:\Users\user\client.pem"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;--tlskey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"C:\Users\user\client.key"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-H&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;dock1:2376&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and running it displays docker system-wide information.&lt;/p&gt;

&lt;p&gt;It works for all docker commands, like&lt;code&gt;dock1 images&lt;/code&gt;, &lt;code&gt;dock1 ps&lt;/code&gt;, &lt;code&gt;dock1 run ....&lt;/code&gt; etc.&lt;/p&gt;
&lt;h3&gt;
  
  
  More fun with batch file
&lt;/h3&gt;

&lt;p&gt;Ok, so the batch file above has some additional lines and variables. Let me explain them:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%~n0&lt;/code&gt; - this is the name of the file without extension. So if have file named &lt;code&gt;dock1.bat&lt;/code&gt; it will have &lt;code&gt;dock1&lt;/code&gt; value. If I name it &lt;code&gt;dock365.bat&lt;/code&gt; it will have &lt;code&gt;dock365&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;set DATADIR=%USERPROFILE%\.docker\%~n0&lt;/code&gt; - I'm using it to set the path where certificates are stored to a folder named the same as the batch file within my user profile &amp;gt; .docker folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if exist "%DATADIR%\host.var" (set /p DOCKERHOST=&amp;lt;"%DATADIR%\host.var") else (set DOCKERHOST=%~n0)&lt;/code&gt; - if hostname of docker server is different from the batch file name, this allows me to put real hostname into the file which will be read into the variable.&lt;br&gt;
So if &lt;code&gt;host.var&lt;/code&gt; file does not exist, &lt;code&gt;%DOCKERHOST%&lt;/code&gt; variable will contain name of the batch file without extension (&lt;code&gt;dock1&lt;/code&gt; for example). But if I want to access docker using &lt;code&gt;dock1.local&lt;/code&gt; hostname, I can put &lt;code&gt;dock1.local&lt;/code&gt; into the &lt;code&gt;host.var&lt;/code&gt; file and then the &lt;code&gt;%DOCKERHOST%&lt;/code&gt; variable will contain  &lt;code&gt;dock1.local&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;set /p DOCKERHOST=&amp;lt;"%DATADIR%\host.var"&lt;/code&gt; - set variable to a value being read from the input file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%*&lt;/code&gt; - passing all arguments that batch file received.&lt;/p&gt;

&lt;p&gt;What this batch script allows me is to have one txt file, let's name it &lt;code&gt;docker_batch.txt&lt;/code&gt;, instead of the batch file.  And then a symbolic link for each docker server I have. Created like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mklink dock1.bat docker_batch.txt
mklink dock365.bat docker_batch.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pretty cool. &lt;/p&gt;
&lt;h2&gt;
  
  
  docker-certs.sh
&lt;/h2&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>docker</category>
      <category>security</category>
    </item>
    <item>
      <title>SED - Update line that starts with but does not contain</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Thu, 09 Apr 2020 18:26:28 +0000</pubDate>
      <link>https://dev.to/milolav/sed-update-line-that-starts-with-but-does-not-contain-m46</link>
      <guid>https://dev.to/milolav/sed-update-line-that-starts-with-but-does-not-contain-m46</guid>
      <description>&lt;p&gt;I wanted to update my docker.service file to include tls certificates in the start command. The idea was to search for the line that starts with &lt;code&gt;ExecStart=&lt;/code&gt; but also not to change anything if that line already contains tcp header &lt;code&gt;-H tcp://&lt;/code&gt;. It turned out to be a bit more complicated than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'\|^ExecStart=.*-H tcp://|! s|^ExecStart=.*|\0 -H tcp://0.0.0.0:2376 --tlsverify --tlscacert='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/ca.pem --tlscert='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/server.pem --tlskey='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/server.key|'&lt;/span&gt; /lib/systemd/system/docker.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Broken in multiple lines for readability&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'\|^ExecStart=.*-H tcp://|!
        s|^ExecStart=.*|\0 -H tcp://0.0.0.0:2376 --tlsverify
        --tlscacert='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/ca.pem --tlscert='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/server.pem
        --tlskey='&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s1"&gt;'/server.key|'&lt;/span&gt; /lib/systemd/system/docker.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In a nutshell, it excludes all lines that start with &lt;code&gt;ExecStart=&lt;/code&gt; and contain &lt;code&gt;-H tcp://&lt;/code&gt; and out of remaining lines, if a line starts with &lt;code&gt;ExecStart=&lt;/code&gt;, it appends additional arguments to the end of the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sed -i&lt;/code&gt; means that we will change a file in place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\|A|! s|S|R|&lt;/code&gt; - using pipes instead of slashes makes it easier when dealing with paths. Note that syntax starts with a backslash when using  special delimiters with addresses (&lt;code&gt;A&lt;/code&gt; part).&lt;br&gt;
Using standard slashes would be like &lt;code&gt;/A/! s/S/R/&lt;/code&gt;, without leading backslash.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\|^ExecStart=.*-H tcp://|!&lt;/code&gt; - using the addresses in sed, exclude lines that start with &lt;code&gt;ExecStart=&lt;/code&gt; and have &lt;code&gt;-H tcp://&lt;/code&gt; down the line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;s|^ExecStart=.*|\0 -H tcp://0.0.0.0:2376 ...&lt;/code&gt; - search for a line that starts with &lt;code&gt;ExecStart=&lt;/code&gt; and grab the rest of the line using  &lt;code&gt;.*&lt;/code&gt; . In the replace part, &lt;code&gt;\0&lt;/code&gt; will hold the complete line match and the rest is additional text that is appended to the line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/lib/systemd/system/docker.service&lt;/code&gt; - file that is being edited&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;For a single line change, a file can be checked if the update is actually required before updating it, and then the exclusion part is not needed at all. But if there are more lines that require similar update this could do the trick.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>sed</category>
    </item>
    <item>
      <title>Installing Windows on VHD to use with Hyper-V</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Fri, 17 Jan 2020 16:38:32 +0000</pubDate>
      <link>https://dev.to/milolav/installing-windows-on-vhd-to-use-with-hyper-v-1j8e</link>
      <guid>https://dev.to/milolav/installing-windows-on-vhd-to-use-with-hyper-v-1j8e</guid>
      <description>&lt;p&gt;One PowerShell script that I've been using for a long time to prepare disk and install Windows for Hyper-V VMs. Since Microsoft released Windows Sandbox I'm not using it as often because sandbox starts faster and for simple application testing that's good enough. But every now and then I need a full VM and here you can find the script I'm using.&lt;/p&gt;

&lt;p&gt;It takes a few of minutes to complete and then a few more minutes for windows setup to complete the first boot. Compared that to manual installation, faster and compared to Hyper-V "Install an OS from a bootable CD/DVD" it's more flexible.&lt;/p&gt;

&lt;p&gt;It does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mounting Installation ISO file&lt;/li&gt;
&lt;li&gt;Presenting a menu to choose edition to install&lt;/li&gt;
&lt;li&gt;Creating virtual hard disk&lt;/li&gt;
&lt;li&gt;Installing windows using DISM tool&lt;/li&gt;
&lt;li&gt;Setting up boot configuration&lt;/li&gt;
&lt;li&gt;Copying unattend.xml&lt;/li&gt;
&lt;li&gt;Cleaning up&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example Usage
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;\Prepare-VirtualWinVhd.ps1&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-VhdFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TestWin.vhdx"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-WinIso&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en_windows_10_multi-edition_version_1709_updated_dec_2017_x64_dvd_100406711.iso"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-WinEdition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Windows 10 Enterprise"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-VhdSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nf"&gt;GB&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-EfiLetter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-VirtualWinLetter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;W&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;-UnattendFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\unattend.xml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Script explained
&lt;/h2&gt;

&lt;p&gt;Some defaults for the script. Installing Windows 10 Enterprise on a 40GB VHD. We will be using drive letter R for Efi partition and W for Windows partition. &lt;strong&gt;Make sure to pick letters are not assigned on your current windows installation&lt;/strong&gt;. In addition to the installation, the unattend.xml file will also be copied to the VHD.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$WinEdition&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;"Windows 10 Enterprise"&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="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$VhdSize&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;40&lt;/span&gt;&lt;span class="nf"&gt;GB&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="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$EfiLetter&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;'R'&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="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&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;'W'&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$UnattendFile&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;"unattend.xml"&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;In the very beginning of the script we are setting &lt;code&gt;$ErrorActionPreference&lt;/code&gt; to &lt;code&gt;Stop&lt;/code&gt; so that it stops on any error. In addition we need to make sure that script is run with elevated privileges. Both DISM and Diskpart require elevated privileges:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="nf"&gt;ActionPreference&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;'Stop'&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;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]([&lt;/span&gt;&lt;span class="no"&gt;Security.Principal.&lt;/span&gt;&lt;span class="kt"&gt;WindowsIdentity&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nf"&gt;GetCurrent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-contains&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'S-1-5-32-544'&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="nf"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Script must be started as an Administrator"&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;h3&gt;
  
  
  Mounting Installation ISO file
&lt;/h3&gt;

&lt;p&gt;Mounting iso file using &lt;code&gt;Mount-DiskImage&lt;/code&gt;, and getting the drive letter of mounted image&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Resolve-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mounting &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$MountResult&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="nf"&gt;Mount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-StorageType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ISO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PassThru&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$DriveLetter&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;$MountResult&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="nf"&gt;Get-Volume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DriveLetter&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="nv"&gt;$DriveLetter&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="nf"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISO file not loaded correctly"&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;Continue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;return&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="nf"&gt;Write-Progress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mounting &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&lt;/span&gt;&lt;span class="s2"&gt; ... Done"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Presenting a menu to choose edition to install
&lt;/h3&gt;

&lt;p&gt;Once iso is mounted, we are using dism tool to enumerate all windows editions stored in install.wim file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$WimFile&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;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$DriveLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\sources\install.wim"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inspecting &lt;/span&gt;&lt;span class="nv"&gt;$WimFile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$WimOutput&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="nf"&gt;dism&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/get-wiminfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/wimfile:&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="nv"&gt;$WimFile&lt;/span&gt;&lt;span class="se"&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="nf"&gt;Out-String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Output of &lt;code&gt;dism /get-wiminfo&lt;/code&gt; command looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\WINDOWS\system32&amp;gt;dism /get-wiminfo /wimfile:f:\sources\install.wim

Deployment Image Servicing and Management tool
Version: 10.0.18362.1

Details for image : f:\sources\install.wim

Index : 1
Name : Windows 10 Education
Description : Windows 10 Education
Size : 14.780.927.379 bytes

Index : 2
Name : Windows 10 Education N
Description : Windows 10 Education N
Size : 13.958.508.090 bytes

Index : 3
Name : Windows 10 Enterprise
Description : Windows 10 Enterprise
Size : 14.781.260.269 bytes

...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So parsing it with regex we want to extract Index and Name&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$WimInfo&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;$WimOutput&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="nf"&gt;Select-String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(?smi)Index : (?&amp;lt;Id&amp;gt;\d+).*?Name : (?&amp;lt;Name&amp;gt;[^&lt;/span&gt;&lt;span class="se"&gt;`r`n&lt;/span&gt;&lt;span class="s2"&gt;]+)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllMatches&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;!&lt;/span&gt;&lt;span class="nv"&gt;$WimInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Matches&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="nf"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Images not found in install.wim&lt;/span&gt;&lt;span class="se"&gt;`r`n&lt;/span&gt;&lt;span class="nv"&gt;$WimOutput&lt;/span&gt;&lt;span class="s2"&gt;"&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;Continue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;return&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 build the menu selecting the Edition that was passed to the script as a parameter just to make things easier. In addition we are checking that selected value is valid.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$Items&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="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;$Menu&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;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$DefaultIndex&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;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$WimInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Matches&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="nf"&gt;ForEach-Object&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;$Items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$Menu&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;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&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="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="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&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="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`r`n&lt;/span&gt;&lt;span class="s2"&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;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&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="nv"&gt;$WinEdition&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;$DefaultIndex&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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&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="nf"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Menu&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;do&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;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;$err&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;$false&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$WimIdx&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="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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$val&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="nf"&gt;Read-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Please select version [&lt;/span&gt;&lt;span class="nv"&gt;$DefaultIndex&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="o"&gt;-eq&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DefaultIndex&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="nv"&gt;$val&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="nv"&gt;$Items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$WimIdx&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;$err&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="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;catch&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;$err&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="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;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$WimIdx&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;It will present a menu like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1) Windows 10 Education
2) Windows 10 Education N
3) Windows 10 Enterprise
4) Windows 10 Enterprise N
5) Windows 10 Pro
6) Windows 10 Pro N
7) Windows 10 Pro Education
8) Windows 10 Pro Education N
9) Windows 10 Pro for Workstations
10) Windows 10 Pro N for Workstations

Please select version [3]:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Creating virtual hard disk
&lt;/h3&gt;

&lt;p&gt;Now that we know what we want to install, it is time to prepare disk for installation. First we ensure that &lt;code&gt;$VhdFile&lt;/code&gt; is full path, expanding it as needed, and that the file does not exists. If filename was not given as a parameter we are using Windows edition name.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&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;!&lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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;$VhdFile&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;$ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SessionState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUnresolvedProviderPathFromPSPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$WimIdx&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="s2"&gt;".vhdx"&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="kr"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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;$ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SessionState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUnresolvedProviderPathFromPSPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="nf"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="nf"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vhd file '&lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&lt;/span&gt;&lt;span class="s2"&gt;' allready exists."&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;Continue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;return&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;Then we create a new VHD using New-VHD command, mount it and get the disk number used in diskpart.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$disk&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="nf"&gt;New-VHD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdSize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nf"&gt;Mount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$disknumber&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="nf"&gt;Get-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="nf"&gt;Get-Disk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Number&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Additionally just as an precaution, make sure that disk number is not 0  which is usually main system disk&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&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;$disknumber&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;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="nf"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unexpected disk number"&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;Continue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="nf"&gt;Out-Null&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;Continue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Out-Null&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;Continue&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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To partition disk we are using a list of commands piped to diskpart. In addition we are assigning known letters to both EFI partition to configure boot record, and to partition for windows installation (future C drive)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="sh"&gt;@"
select disk &lt;/span&gt;&lt;span class="nv"&gt;$DiskNumber&lt;/span&gt;&lt;span class="sh"&gt;
convert gpt
select partition 1
delete partition override
create partition primary size=300
format quick fs=ntfs
create partition efi size=100
format quick fs=fat32
assign letter="&lt;/span&gt;&lt;span class="nv"&gt;$EfiLetter&lt;/span&gt;&lt;span class="sh"&gt;"
create partition msr size=128
create partition primary
format quick fs=ntfs
assign letter="&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="sh"&gt;"
exit
"@&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="nf"&gt;diskpart&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Installing windows using DISM tool
&lt;/h3&gt;

&lt;p&gt;Once the disk is ready we can apply image (install Windows) using dism tool. Since we are using all those variables it makes sense to build command line as a string and run it using &lt;code&gt;Invoke-Expression&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nf"&gt;Invoke-Expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dism /apply-image /imagefile:&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="nv"&gt;$WimFile&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="s2"&gt; /index:&lt;/span&gt;&lt;span class="nv"&gt;$WimIdx&lt;/span&gt;&lt;span class="s2"&gt; /applydir:&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Setting up boot configuration
&lt;/h3&gt;

&lt;p&gt;And then we make disk bootable&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nf"&gt;Invoke-Expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\windows\system32\bcdboot &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\windows /f uefi /s &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$EfiLetter&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="nf"&gt;Invoke-Expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bcdedit /store &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$EfiLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\EFI\Microsoft\Boot\BCD"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Copying unattend.xml
&lt;/h3&gt;

&lt;p&gt;If there was unatted.xml file set, make sure to copy it so Windows can be configured on the first boot.&lt;br&gt;
&lt;/p&gt;
&lt;div class="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="nv"&gt;$UnattendFile&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="nf"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Copying unattended.xml"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nf"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"directory"&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="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\Windows\Panther\"&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;Copy-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$UnattendFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:\Windows\Panther\unattend.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="nf"&gt;Out-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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cleaning up
&lt;/h3&gt;

&lt;p&gt;In the end we just remove all assigned letters and unmount everything&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="sh"&gt;@"
select disk &lt;/span&gt;&lt;span class="nv"&gt;$disknumber&lt;/span&gt;&lt;span class="sh"&gt;
select partition 2
remove letter="&lt;/span&gt;&lt;span class="nv"&gt;$EfiLetter&lt;/span&gt;&lt;span class="sh"&gt;"
select partition 4
remove letter="&lt;/span&gt;&lt;span class="nv"&gt;$VirtualWinLetter&lt;/span&gt;&lt;span class="sh"&gt;"
exit
"@&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="nf"&gt;diskpart&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VhdFile&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Dismount-DiskImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ImagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WinIso&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="nf"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Unattend.xml
&lt;/h2&gt;

&lt;p&gt;Simple unattend.xml that just sets user  (name &lt;code&gt;user&lt;/code&gt;, password &lt;code&gt;login&lt;/code&gt;) and skips all installation questions. There are many more options to set. You can find more from Microsoft &lt;a href="https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/update-windows-settings-and-scripts-create-your-own-answer-file-sxs"&gt;Answer files (unattend.xml)&lt;/a&gt;  or use online tool &lt;a href="https://www.windowsafg.com/"&gt;Windows Answer File Generator&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="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;unattend&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"urn:schemas-microsoft-com:unattend"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;settings&lt;/span&gt; &lt;span class="na"&gt;pass=&lt;/span&gt;&lt;span class="s"&gt;"oobeSystem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft-Windows-Shell-Setup"&lt;/span&gt; &lt;span class="na"&gt;processorArchitecture=&lt;/span&gt;&lt;span class="s"&gt;"amd64"&lt;/span&gt; &lt;span class="na"&gt;publicKeyToken=&lt;/span&gt;&lt;span class="s"&gt;"31bf3856ad364e35"&lt;/span&gt;
               &lt;span class="na"&gt;language=&lt;/span&gt;&lt;span class="s"&gt;"neutral"&lt;/span&gt; &lt;span class="na"&gt;versionScope=&lt;/span&gt;&lt;span class="s"&gt;"nonSxS"&lt;/span&gt; &lt;span class="na"&gt;xmlns:wcm=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/WMIConfig/2002/State"&lt;/span&gt;
               &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;AutoLogon&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Password&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;login&lt;span class="nt"&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;PlainText&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PlainText&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Password&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/Enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Username&amp;gt;&lt;/span&gt;user&lt;span class="nt"&gt;&amp;lt;/Username&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/AutoLogon&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;OOBE&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;HideEULAPage&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/HideEULAPage&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;HideOEMRegistrationScreen&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/HideOEMRegistrationScreen&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;HideOnlineAccountScreens&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/HideOnlineAccountScreens&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;HideWirelessSetupInOOBE&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/HideWirelessSetupInOOBE&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;NetworkLocation&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/NetworkLocation&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SkipUserOOBE&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/SkipUserOOBE&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SkipMachineOOBE&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/SkipMachineOOBE&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ProtectYourPC&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/ProtectYourPC&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/OOBE&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;UserAccounts&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;LocalAccounts&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;LocalAccount&lt;/span&gt; &lt;span class="na"&gt;wcm:action=&lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Password&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;login&lt;span class="nt"&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;PlainText&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PlainText&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/Password&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Description&amp;gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;DisplayName&amp;gt;&lt;/span&gt;user&lt;span class="nt"&gt;&amp;lt;/DisplayName&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Group&amp;gt;&lt;/span&gt;Administrators&lt;span class="nt"&gt;&amp;lt;/Group&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;user&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/LocalAccount&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/LocalAccounts&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/UserAccounts&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;RegisteredOrganization&amp;gt;&amp;lt;/RegisteredOrganization&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;RegisteredOwner&amp;gt;&amp;lt;/RegisteredOwner&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;DisableAutoDaylightTimeSet&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/DisableAutoDaylightTimeSet&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;TimeZone&amp;gt;&lt;/span&gt;GMT Standard Time&lt;span class="nt"&gt;&amp;lt;/TimeZone&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/unattend&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Final script
&lt;/h2&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>powershell</category>
      <category>vhd</category>
    </item>
    <item>
      <title>Downloading GAL as individual vCards using OWA</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Sun, 12 Jan 2020 19:30:21 +0000</pubDate>
      <link>https://dev.to/milolav/downloading-gal-as-individual-vcards-using-owa-5aoi</link>
      <guid>https://dev.to/milolav/downloading-gal-as-individual-vcards-using-owa-5aoi</guid>
      <description>&lt;p&gt;Some time ago, a friend asked me about syncing Exchanges's GAL (Global Address List) with a mobile phone. So I started looking into Exchange Web Services, but as it turns out I need GAL id in order to query it.  And to get it I need either more privileged access to Office 365 tenant or snoop OWA's traffic as described in the article: &lt;a href="https://gsexdev.blogspot.com/2013/05/using-ews-findpeople-operation-in.html"&gt;https://gsexdev.blogspot.com/2013/05/using-ews-findpeople-operation-in.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the network log in a browser I figured that I could effectively do everything within a browser ran from Puppeteer and send it to Radicale CardDAV server.&lt;/p&gt;

&lt;p&gt;But as it turned out, one required field, "Pager", was not present in Exchange's Persona object. So good old "copy everything from GAL into Contacts" worked, at least for one-off sync, and this idea was abandoned.&lt;/p&gt;

&lt;p&gt;Here is JavaScript PoC that can be used to download GAL as vCard files. Bear in mind that it doesn't have any error handling and might not work in every browser. It works in Chrome. Generated vCards are not 100% by specification since I'm serializing whole persona object into it, but I haven't had any issue opening it in Windows (People app), Outlook or on iOS/Android phones.&lt;/p&gt;

&lt;p&gt;Before posting this I just cleaned up code a bit and replaced XHR with fetch. &lt;/p&gt;

&lt;p&gt;How to use in 5 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://outlook.office365.com/people/"&gt;https://outlook.office365.com/people/&lt;/a&gt; in a browser and sign in with your O365 account&lt;/li&gt;
&lt;li&gt;Open browser's console&lt;/li&gt;
&lt;li&gt;Paste this script&lt;/li&gt;
&lt;li&gt;Run one of the functions, suggestion is to test with OwaGalExtractor.downloadGal(null, 5)&lt;/li&gt;
&lt;li&gt;Look at a bunch of file being downloaded (chrome might ask you to allow multiple file download) &lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>owa</category>
      <category>gal</category>
      <category>vcard</category>
    </item>
    <item>
      <title>Making Dynamics 365 CE reports available in UCI on iOS devices
</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Sat, 11 Jan 2020 00:06:13 +0000</pubDate>
      <link>https://dev.to/milolav/making-dynamics-365-ce-reports-available-in-uci-on-ios-devices-25km</link>
      <guid>https://dev.to/milolav/making-dynamics-365-ce-reports-available-in-uci-on-ios-devices-25km</guid>
      <description>&lt;p&gt;Some time ago, working on a Dynamics 365 CE project, I wanted to display reports within the app on an iOS device. Originally posted in May 2019 (&lt;a href="https://gist.github.com/milolav/14fc9a27e0d3ebc8f15ae992dff9f182"&gt;https://gist.github.com/milolav/14fc9a27e0d3ebc8f15ae992dff9f182&lt;/a&gt;)&lt;br&gt;
but looks like dev.to is a better place to share ideas, so here goes...&lt;/p&gt;

&lt;p&gt;At the time of writing, Microsoft does not officially support running reports in an app on iOS / Android / Windows devices in the Unified Interface (UCI), but as it turns out, it's actually possible to display a report within the app using SiteMap entries pointing to Web Resources.&lt;/p&gt;

&lt;p&gt;Reports are just another url in Dynamics and it seems the only reason why is that not working in the app is because every time you click on a item in the reports view it opens it in a new window. &lt;/p&gt;
&lt;h2&gt;
  
  
  First Idea
&lt;/h2&gt;

&lt;p&gt;The first idea was just to use url of specific report, put it into Site Map like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;/crmreports/viewer/viewer.aspx?action=filter&amp;amp;helpID=User Summary.rdl&amp;amp;id={e5838060-8866-e911-a819-000d3ab29ccc}&amp;amp;appid=3ef28a1b-5666-e911-a9c5-000d3ab78b73&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem is that menu items with this url do not show in the menu.&lt;/p&gt;
&lt;h2&gt;
  
  
  Web Resource to the rescue
&lt;/h2&gt;

&lt;p&gt;Second idea was just to create a simple web resource and put in the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/crmreports/viewer/viewer.aspx?action=filter&amp;amp;helpID=User Summary.rdl&amp;amp;id={e5838060-8866-e911-a819-000d3ab29ccc}"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 100%; width: 100%;"&lt;/span&gt; &lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And that worked!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since it is not preferable to to have a separate web resource for every report next challenge was to pass data to that web resource.  Microsoft provides sample on how to pass data to web resources outlined here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/sample-pass-multiple-values-web-resource-through-data-parameter"&gt;https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/sample-pass-multiple-values-web-resource-through-data-parameter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's more interesting, passing data parameter also works in the Site Map. Not through the visual editor in Dynamics, but it does work by directly editing the SiteMap XML or using SiteMap Editor in XRMToolBox. Just adding data parameter after the web resource name like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$webresource:new_reportwrapper.html?data=mydatavalue1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which means that report guid value can be passed like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$webresource:new_reportwrapper.html?data=e5838060-8866-e911-a819-000d3ab29ccc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the only thing left is to make use of passed value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Web Resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background: white; position: absolute; margin: 0.5em; top: 0; left: 0; right: 0; bottom: 0;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loadReport&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;!=&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="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&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="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\+&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;kv&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="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;guid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kv&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/crmreports/viewer/viewer.aspx?id=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&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;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;msg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Report parameters not set!&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onreadystatechange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading_overlay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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="nx"&gt;loadReport&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"report"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 100%; width: 100%;"&lt;/span&gt; &lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"loading_overlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&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;"background: none repeat scroll 0 0 black; position: fixed; display: block; opacity: 0.5; left: 0; top: 0; height: 100%; width: 100%;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;LOADING ...&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Site Map item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;SubArea&lt;/span&gt;
  &lt;span class="na"&gt;Id=&lt;/span&gt;&lt;span class="s"&gt;"tempId_636927761987954911"&lt;/span&gt;
  &lt;span class="na"&gt;Url=&lt;/span&gt;&lt;span class="s"&gt;"$webresource:new_reportwrapper.html?data=e5838060-8866-e911-a819-000d3ab29ccc"&lt;/span&gt;
  &lt;span class="na"&gt;Title=&lt;/span&gt;&lt;span class="s"&gt;"User Summary Report"&lt;/span&gt;
  &lt;span class="na"&gt;AvailableOffline=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
  &lt;span class="na"&gt;PassParams=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
  &lt;span class="na"&gt;Client=&lt;/span&gt;&lt;span class="s"&gt;"All"&lt;/span&gt;
  &lt;span class="na"&gt;Sku=&lt;/span&gt;&lt;span class="s"&gt;"All"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vkUjv6ep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.postimg.cc/mDVQs0w6/ipad-report.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vkUjv6ep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.postimg.cc/mDVQs0w6/ipad-report.gif" alt="ipad report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional stuff
&lt;/h2&gt;

&lt;p&gt;This approach can also be used for getting diag tool inside the app by creating a web resource  with the following code and linking that in the site map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/tools/diagnostics/diag.aspx"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 100%; width: 100%;"&lt;/span&gt; &lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which allows you to access it from within the app rather than typing or pasting this in a browser.&lt;/p&gt;

</description>
      <category>dynamics365</category>
      <category>reports</category>
    </item>
    <item>
      <title>Tracking Service Updates for MS Dynamics 365 CE in calendar</title>
      <dc:creator>Miro</dc:creator>
      <pubDate>Sat, 11 Jan 2020 00:06:00 +0000</pubDate>
      <link>https://dev.to/milolav/tracking-service-updates-for-ms-dynamics-365-ce-in-calendar-8ih</link>
      <guid>https://dev.to/milolav/tracking-service-updates-for-ms-dynamics-365-ce-in-calendar-8ih</guid>
      <description>&lt;p&gt;Another script from a couple of months ago&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With Microsoft pushing "Service Update XX" almost every week it became hard to track down when particular update is going to be released.&lt;/p&gt;

&lt;p&gt;Powershell script uses application registered in AAD with privileges to read messages from Office 365 Communication API and access D365 to create appointments. Appointments are set category and subcategory to identify unique messages without customizations. Recipients of the appointment are defined as a list of activity parties.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>powershell</category>
      <category>dynamics365</category>
    </item>
  </channel>
</rss>
