<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Dev Notes]]></title><description><![CDATA[Thoughts, stories and ideas.]]></description><link>https://blog.frankfu.com.au/</link><image><url>https://blog.frankfu.com.au/favicon.png</url><title>Dev Notes</title><link>https://blog.frankfu.com.au/</link></image><generator>Ghost 2.38</generator><lastBuildDate>Sat, 28 Jun 2025 16:37:14 GMT</lastBuildDate><atom:link href="https://blog.frankfu.com.au/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Setting up Jenkins on Cloud with Junit and Jacoco integrated via Github]]></title><description><![CDATA[<h1 id="summary">Summary</h1><p>The notes below are complimentary to the youtube video showing students how to setup <code>Jenkins</code> on the cloud along with</p><ul><li>Setting up <code>Gradle</code> to run builds on <code>Jenkins</code></li><li>Setting up <code>Junit</code> to generate a <strong>test report</strong></li><li>Setting up <code>Jacoco</code> to generate a <strong>code coverage report</strong></li><li>Excluding classes from the</li></ul>]]></description><link>https://blog.frankfu.com.au/2022/09/15/setting-up-jenkins-on-cloud-with-junit-and-jacoco-integrated-via-github/</link><guid isPermaLink="false">6322da53226a210001de86d5</guid><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Thu, 15 Sep 2022 07:56:07 GMT</pubDate><content:encoded><![CDATA[<h1 id="summary">Summary</h1><p>The notes below are complimentary to the youtube video showing students how to setup <code>Jenkins</code> on the cloud along with</p><ul><li>Setting up <code>Gradle</code> to run builds on <code>Jenkins</code></li><li>Setting up <code>Junit</code> to generate a <strong>test report</strong></li><li>Setting up <code>Jacoco</code> to generate a <strong>code coverage report</strong></li><li>Excluding classes from the <code>Jacoco</code> <strong>code coverage report</strong></li><li>Setting up a <code>Webhook</code> from <code>Github</code> to <code>Jenkins</code></li><li>Setting up a <code>jar</code> file as output of a jenkins build</li><li>Configuring <code>Gradle</code> to wait for user input</li></ul><h2 id="the-video">The Video</h2><p><a href="https://www.youtube.com/watch?v=fCnmcsc7VEc">https://www.youtube.com/watch?v=fCnmcsc7VEc</a></p><h2 id="on-your-browser">On your browser</h2><p></p><ol><li> Create an account on <a href="https://www.vultr.com/">Vultr</a> </li><li> Head to <code>Products &gt; Plus button &gt; Deploy new server</code>  then choose <strong>Server</strong> - <code>Cloud compute</code> , <strong>Server location</strong> - <code>Sydney</code>, <strong>Server type</strong> - <code>Marketplace  Apps &gt; Docker &gt; Ubuntu 20.04 x64</code>  </li><li> Head to <code>Products &gt; Server &gt; Server Details</code> and note down <code>ip address</code>,  <code>username</code> and <code>password</code> It should look something like <code>107.191.57.92</code> <code>root</code> and <code>v3j4by@341</code> respectively </li></ol><h2 id="on-your-terminal">On your terminal</h2><p></p><ul><li> Remote into your newly created instance via <code>ssh</code> </li></ul><pre><code>ssh &lt;username&gt;@&lt;ip address&gt;  </code></pre><ul><li> Create a docker container with Jenkins official image and give it a custom name <code>myjenkins</code>  , do this by executing the command </li></ul><pre><code>docker run -d --restart always -p 80:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home --name myjenkins jenkins/jenkins:lts-jdk11</code></pre><ul><li> Find your initial password by executing the command below, it should look something like <code>a1234b32b32412asdf123vbdfgasfqwer</code> </li></ul><pre><code>docker exec myjenkins cat /var/jenkins_home/secrets/initialAdminPassword</code></pre><h2 id="on-your-browser-1">On your browser</h2><ol><li>Open your browser and head to <code>&lt;ip address&gt;</code> e.g. <code>http://107.191.57.92</code></li><li>It should ask for your <code>initial password</code> that you copied in the previous steps  Choose <code>Install Suggested Plugins</code> (takes around 10mins) Create a <code>new</code> username and password for web access to your newly created Jenkins instance Share the <code>ip address</code> together with your <code>new</code> username and password with your team members so they can see faillure and success of builds  </li><li>Adding support for <code>Jacoco</code>  Head to <code>Jenkins &gt; Manage Jenkins&gt; Manage Plugins &gt; Available &gt;</code> and Type '<strong>Jacoco</strong>', select the checkbox then click <code>Install without restart</code> Click <code>Restart Jenkins when installation is complete and no jobs are running</code>  </li><li>Adding support for <code>Gradle</code>  Head to <code>Manage Jenkins &gt; Global Tool Configuration &gt; Gradle &gt; Add Gradle &gt;</code> Type any name e.g. <code>mygradle691</code>  </li><li>Adding support for <code>Github Enterprise</code>  Head to <code>Manage Jenkins &gt; Configure System &gt; GitHub Enterprise Servers &gt;</code> Set API Endpoint to  <a href="https://github.sydney.edu.au/api/v3"><code>https://github.sydney.edu.au/api/v3</code></a> Set Name to <code>Github USYD</code>  </li></ol><h2 id="setting-up-the-webhook-from-github-to-jenkins">Setting up the webhook from Github to Jenkins</h2><ul><li>Repo  <a href="https://github.sydney.edu.au/FRFU8670/fruitshop.git">https://github.sydney.edu.au/FRFU8670/fruitshop.git</a></li><li>Payload URL - http://&lt;MY IP ADDRESS&gt;/github-webhook/</li><li>Content type : application/json</li><li>Disable SSL verification</li></ul><h2 id="faq">FAQ</h2><p><strong>How can I ask Jenkins to pull down my source code from University of Sydney’s Github?</strong></p><ul><li>Ensure that you have configured Github Enterprise in <a href="https://www.notion.so/Setup-Jenkins-server-on-the-cloud-9682b3823b0d445caa11192cd05f9ad9">step 5 of “<strong>on your browser</strong>”</a></li><li><code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Source Code Management</code>  Choose <code>Git</code> Repository URL - <a href="https://github.sydney.edu.au/FRFU8670/fruitshop.git"><code>https://github.sydney.edu.au/FRFU8670/fruitshop.git</code></a> Setup your credentials Credentials  Click on <code>Add</code> &gt; <code>Jenkin</code> and fill in your  Username : frfu8670 (your unikey) Password  : xxxxxxxx (your unikey password) Click on <code>Add</code>   Click on the dropdown menu and choose your newly added credentials e.g. <code>frfu8670/*****</code>    </li></ul><p><strong>How can I ask Jenkins to run the tasks to build and test my project?</strong></p><ul><li>Ensure you have gradle configured properly for Jenkins - see <a href="https://www.notion.so/Setup-Jenkins-server-on-the-cloud-9682b3823b0d445caa11192cd05f9ad9">step 4  in <strong>“On your browser”</strong></a></li><li>Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Build</code> &gt; <code>Invoke Gradle script</code> &gt; <code>Invoke Gradle</code> &gt; <code>Gradle version</code> and select the gradle that you configured in step 4 of “On your browser” e.g. <code>mygradle691</code></li><li>Then under <code>Tasks</code> section, type in <code>clean build test</code> which will run 3 separate tasks that will clean your project, build your project and run unit tests on said project</li></ul><p><strong>How can I add a report to show my unit test results?</strong></p><ul><li> Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Post-build Actions</code> &gt; <code>Publish Junit test result report</code>  and find the section that says <strong>Test report XMLs</strong> and enter the following and press <code>Save</code> </li></ul><pre><code>**/test-results/**/*.xml</code></pre><p><strong>How can I add a report to show my unit test coverage?</strong></p><ul><li>Ensure you have the Jacoco plugin installed for Jenkins - see <a href="https://www.notion.so/Setup-Jenkins-server-on-the-cloud-9682b3823b0d445caa11192cd05f9ad9">step 3 in  <strong>“On your browser”</strong></a></li><li>Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Post-build Actions</code> &gt; <code>Record JaCoCo coverage report</code> and press <code>Save</code></li></ul><p><strong>How can I exclude a class from being included in the Test Coverage Report?</strong></p><ul><li> Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Post-build Actions</code> &gt; <code>Record JaCoCo coverage report</code> and add the following under Exclusions. For example if you had a class called <code>App.java</code> and which had non backend business logic such as asking for command line user input and cleaning and you wish to exclude in your test coverage report  you can enter the following in the input field undernearth the Exclusions and press <code>Save</code> </li></ul><pre><code>**/App.class</code></pre><p><strong>How can I ask Github to notify Jenkins when a new commit has been pushed?</strong></p><ul><li>Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Build Triggers</code>  Choose <code>GitHub hook trigger for GITScm polling</code> and press <code>Save</code>  </li><li>Head over to <code>Github</code> &gt; <code>Your repository</code> &gt; <code>Settings</code> &gt; <code>Hooks</code> &gt; <code>Add webhook</code>  <em>Payload URL</em> - <code>http://&lt;your server ip&gt;/github-webhook/</code> e.g. <a href="http://107.191.57.92/github-webhook/"><code>http://107.191.57.92/github-webhook/</code></a> then choose <em>Content type</em> - <code>application/json</code> , <em>SSL Verification</em> - <code>Disable</code> then click on <code>Add webhook</code>  </li></ul><p><strong>How can I ask Jenkins to find out if a new commit has been pushed without Github notifying it?</strong></p><ul><li>Head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Build Triggers</code>  Choose Poll SCM Type in <strong><code>H/1 * * * *</code></strong> underneath <code>Schedule</code>  </li><li>This will ask Jenkins to poll your repository every 1 minute for changes such as new  commit pushed and merged pull requests etc.</li></ul><p><strong>How can I ask Jenkins to build a <code>jar</code> file that contains my command line app?</strong></p><ul><li> Head over to project code and add  a <code>jar</code> task in your <code>app/build.gradle</code> file <code>jar </code></li></ul><pre><code>jar {
    manifest {
        attributes(
            &quot;Main-Class&quot;: 'fruitshop.App'
        )
    }
}
</code></pre>
<ul><li> Then head over to <code>Jenkins</code> &gt; <code>Your project</code> &gt; <code>Configure</code> &gt; <code>Add post-build action</code>  Choose <code>Archive the artifacts</code> Under <code>Files to archive</code> type in <code>app/build/libs/app.jar</code> or whatever the name of your app is called  </li></ul><p><strong>How can I ask Gradle to wait for user input?</strong></p><ul><li>Head over to your <code>app/build.gradle</code> file and give some configuration options to the <code>run</code> task</li></ul><pre><code>run {
   standardInput = System.in
}</code></pre><ul><li>Then every time you would like to run your application instead of <code>gradle run</code> you would execute </li></ul><pre><code>gradle run --console=plain</code></pre>]]></content:encoded></item><item><title><![CDATA[Moving from Windows 1809's OpenSSH to OpenSSH Portable]]></title><description><![CDATA[Moving from Windows 1809's OpenSSH to OpenSSH Portable]]></description><link>https://blog.frankfu.com.au/2019/03/21/moving-from-windows-1809s-openssh-to-openssh-portable/</link><guid isPermaLink="false">5c931a62f338cf0001e0e668</guid><category><![CDATA[ssh]]></category><category><![CDATA[windows]]></category><category><![CDATA[windows-server]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Thu, 21 Mar 2019 05:22:14 GMT</pubDate><content:encoded><![CDATA[<p>I'm using Git for Windows and have configured the git client to use a custom ssh command via <code>core.sshCommand</code> to look for the ssh binary that has been installed as part of <a href="https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse">Windows 10's 1809's Optional Feature</a> this has been working well but recently when I'm trying to use git against Github as a remote repo (Bitbucket worked fine) it has been giving me warning messages</p>
<blockquote>
<p>warning: agent returned different signature type ssh-rsa (expected rsa-sha2-512)</p>
</blockquote>
<p>After performing the <a href="https://www.google.com/search?q=warning%3A+agent+returned+different+signature+type+ssh-rsa+(expected+rsa-sha2-512)">obligatory google search</a>, I stumbled across several threads but still couldn't find a solution. This annoyed me enough to look for other alternatives and discovered a <a href="https://github.com/PowerShell/openssh-portable">Windows port</a> of <a href="https://github.com/openssh/openssh-portable">OpenSSH Portable</a>. So since I didn't want (probably not even possible) to have both binaries (and  ssh-agents) running side by side. I decided it would be best that I remove Window's 1809's installed version first then install OpenSSH Portable</p>
<h2 id="uninstallwindowsopenssh">Uninstall Windows' OpenSSH</h2>
<p>This basically removes the binaries installed in <code>C:\Windows\System32\OpenSSH</code></p>
<pre><code class="language-powershell">
Remove-WindowsCapability -Online -Name &quot;OpenSSH.Client~~~~0.0.1.0&quot;
Remove-WindowsCapability -Online -Name &quot;OpenSSH.Server~~~~0.0.1.0&quot;

</code></pre>
<p>Stop and delete the existing Windows Service</p>
<pre><code class="language-powershell">Get-Service ssh-agent | Stop-Service
sc.exe delete ssh-agent
</code></pre>
<h2 id="installopensshportable">Install OpenSSH Portable</h2>
<p>I'm using <a href="https://chocolatey.org/packages/openssh">Chocolatey's openssh package</a> but you can <a href="https://github.com/PowerShell/openssh-portable">download and install the binaries yourself here</a>. This installs the binaries into <code>C:\Program Files\OpenSSH-Win64</code> . The <code>/SSHAgentFeature</code> flag ensures that the SSH Agent Service gets installed too.</p>
<pre><code class="language-powershell">choco install openssh --package-parameters=&quot;/SSHAgentFeature&quot;
</code></pre>
<p>Note version <a href="https://chocolatey.org/packages/openssh/7.9.0.1"><code>7.9.0.1</code></a> was the last good working version for me. <code>8.0.0.1</code> was complaining about my ssh key being in an invalid format.</p>
<p>Check that you have the binaries installed</p>
<pre><code>Get-Command ssh*exe
    
    CommandType Name              Version Source
    ----------- ----              ------- ------
    Application ssh.exe           7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh.exe
    Application ssh-add.exe       7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh-add.exe
    Application ssh-agent.exe     7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh-agent.exe
    Application sshd.exe          7.9.0.0 C:\Program Files\OpenSSH-Win64\sshd.exe
    Application ssh-keygen.exe    7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh-keygen.exe
    Application ssh-keyscan.exe   7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh-keyscan.exe
    Application ssh-shellhost.exe 7.9.0.0 C:\Program Files\OpenSSH-Win64\ssh-shellhost.exe
</code></pre>
<p>Check that you have the <code>ssh-agent</code> running</p>
<pre><code>Get-Service ssh-agent

Status   Name               DisplayName
------   ----               -----------
Running  ssh-agent          ssh-agent
</code></pre>
<p>Configure our git client to use our newly installed binaries for ssh</p>
<pre><code>git config --global core.sshCommand &quot;'C:\Program Files\OpenSSH-Win64\ssh.exe'&quot;
</code></pre>
<p>Ensure that it has been configured properly</p>
<pre><code>Get-Content $env:USERPROFILE\.gitconfig | Select-String sshCommand -Context 6

[core]
        excludesfile = ~/.gitignore_global
        sshCommand = 'C:\\Program Files\\OpenSSH-Win64\\ssh.exe'
</code></pre>
<p>I wanted to remove any existing identities (but this could be optional)</p>
<pre><code>ssh-add -D
</code></pre>
<p>Add new identites</p>
<pre><code>ssh-add 
</code></pre>
<p>Then we should be good to go ;)</p>
]]></content:encoded></item><item><title><![CDATA[Modifying User-Agent based on Bot or Human for CloudFront via Lambda@Edge]]></title><description><![CDATA[<h2 id="the-problem">The problem</h2><p>We are currently using <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html">AWS Cloudfront</a> to front our backend .NET web application (the origin). A new requirement came through asking us to serve up customised content based on whether the originating request was from a <strong>bot </strong>or <strong>human</strong>.</p><p>By default, <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web">AWS Cloudfront does not forward </a>the user</p>]]></description><link>https://blog.frankfu.com.au/2019/03/05/modifying-user-agent-based-on-bot-or-human-for-cloudfront-via-lambda-edge/</link><guid isPermaLink="false">5c7df336f338cf0001e0e5a6</guid><category><![CDATA[aws]]></category><category><![CDATA[cloudfront]]></category><category><![CDATA[aws-lambda]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[js]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Tue, 05 Mar 2019 04:49:16 GMT</pubDate><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2><p>We are currently using <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html">AWS Cloudfront</a> to front our backend .NET web application (the origin). A new requirement came through asking us to serve up customised content based on whether the originating request was from a <strong>bot </strong>or <strong>human</strong>.</p><p>By default, <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web">AWS Cloudfront does not forward </a>the user request's <code>User-Agent</code> to the origin so what can we do? Well one (non-peformant) way is to simply modify our distribution behavior to whitelist the the <code>User-Agent</code> header but this puts a punch to our hit/miss ratio because different users with different browser versions and device types will generated a bunch of different user agents (e.g. Safari on MacOS, Opera on Smart TV, Chrome on iPad etc).</p><h2 id="the-solution">The solution</h2><p>So how can we let our origin serve up customised content for <strong>bots</strong> yet stay (somewhat) performant? <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html">Lambda@Edge </a>to the rescue! Lambda@Edge lets you run Lambda functions to customize content that CloudFront delivers by executing the functions at <a href="https://aws.amazon.com/cloudfront/features/">edge locations</a>. You can modify the request/response during any of the following stages:</p><ul><li>After CloudFront receives a request from a viewer (viewer request)</li><li>Before CloudFront forwards the request to the origin (origin request)</li><li>After CloudFront receives the response from the origin (origin response)</li><li>Before CloudFront forwards the response to the viewer (viewer response)</li></ul><p>My proposed solution still requires us to whitelist the <code>User-Agent</code> header but additionally modify the <em>viewer request</em> by setting the <code>User-Agent</code> to <code>Human</code> if it doesn't match any whitelisted bot. If a whitelisted bot is matched then keep the bot's <code>User-agent</code> and forward it to our origin for further content customisation. This way, real humans will always have a <code>User-Agent</code> of <code>Human</code> and not the device/browser/version variant (e.g. Safari on MacOS, Opera on Smart TV, Chrome on iPad etc) while being able to identify the <code>User-Agent</code> of the whitelisted bot at the same time. I've created the the Lambda function below which is deployed to my Cloudfront Distribution.</p><p></p><h3 id="step-1">Step 1 </h3><p>Whitelist the <code>User-Agent</code> header in our distributions behavior over on our AWS console (or AWS CLI)</p><p><code>CloudFront Distributions &gt; E2VZ0ISAMPLEDST &gt; Behaviors &gt; Edit &gt; Whitelist Headers &gt; Enter a custom header "User-Agent" &gt; Add Custom &gt; Yes, Edit</code></p><h3 id="step-2">Step 2 </h3><p>Create and deploy our Lambda function. If you are new to Lambda functions like I was, simply follow their tutorial to <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html">creating a simple Lambda@Edge function</a>. Once you are comfortable creating a Lambda function, you can create your own. Here is mine.</p><p><strong>Lambda Function.  </strong><em>Triggered </em>w<em>hen CloudFront receives a request from a viewer (viewer request)</em></p><pre><code class="language-javascript">'use strict';
 
exports.handler = (event, context, callback) =&gt; {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const uri = request['uri'];
    const botPattern = &quot;Googlebot\\/|Googlebot-Mobile|Googlebot-Image|Googlebot-News|Googlebot-Video|bingbot&quot;;
    var re = new RegExp(botPattern, 'i');
    var userAgent = headers['user-agent'][0]['value'];

    if (re.test(userAgent)) {
        headers['user-agent'] = [{key: 'User-Agent', value: userAgent}];
    } else {
        headers['user-agent'] = [{key: 'User-Agent', value: 'Human'}];
    }

    callback(null, request);
};
</code></pre>
<p>Now obviously the <code>botPattern</code> isn't an exhaustive list because it's my personal whitelist. You can implement your own logic however you like ;) </p><p>If anyone has suggestions to improve it, please leave your comments below!</p><h2 id="references">References</h2><p><a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-how-to-choose-event.html">https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-how-to-choose-event.html</a></p>]]></content:encoded></item><item><title><![CDATA[Creating a Ubuntu Server as a guest OS on Hyper-V Host with Secure Boot via Powershell]]></title><description><![CDATA[<p>I was recently curious about <strong>Secure Boot, </strong>a mechanism that starts the bootloader only if the bootloader’s signature has maintained integrity, assuring that only approved components are allowed to run. I was pleasantly surprised to find that it was supported on <a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows">most Linux distributions.</a> The important take away here</p>]]></description><link>https://blog.frankfu.com.au/2019/02/13/creating-a-ubuntu-server-as-a-guest-on-hyper-v-host/</link><guid isPermaLink="false">5c636f6df338cf0001e0e534</guid><category><![CDATA[hyper-v]]></category><category><![CDATA[ubuntu]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Wed, 13 Feb 2019 01:17:39 GMT</pubDate><content:encoded><![CDATA[<p>I was recently curious about <strong>Secure Boot, </strong>a mechanism that starts the bootloader only if the bootloader’s signature has maintained integrity, assuring that only approved components are allowed to run. I was pleasantly surprised to find that it was supported on <a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows">most Linux distributions.</a> The important take away here is to configure the VM to <a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/what-s-new-in-hyper-v-on-windows#BKMK_linux">use the Microsoft UEFI Certificate Authority<strong>.</strong></a></p><p>If you have an existing VM, you can enable it by</p><pre><code>Set-VMFirmware TestVM -SecureBootTemplate MicrosoftUEFICertificateAuthority
</code></pre><p>If you want to start from scratch you can follow the script below, it assumes that you've already created a <a href="https://docs.microsoft.com/en-us/powershell/module/hyper-v/new-vmswitch?view=win10-ps">virtual switch</a> and have downloaded a copy of Ubuntu Server</p><h2 id="createandconfigure">Create and configure</h2>
<pre><code class="language-powershell"># VM creation
$vmName = &quot;UBUSRV&quot;;
$vmNewDiskPath = &quot;F:\HyperV\Virtual Hard Disks\UBUSRV.vhdx&quot;;
$vmNewDiskSize = 20GB;
$vmPath = &quot;F:\HyperV\Virtual Machines&quot;;
$vmGeneration = 2;
$vmBootDevice = &quot;VHD&quot;;
$vmSwitchName = &quot;MyVirtualSwitch&quot;; # To find existing switches run, Get-VMSwitch | ft
$vmDvdDrivePath = &quot;C:\Users\Frank\Downloads\ubuntu-18.04.1.0-live-server-amd64.iso&quot;

$vmFirmwareEnableSecureBoot = &quot;On&quot;; # Turn off if you trust and/or image isn't supported.
$vmFirmwareSecureBootTemplate = &quot;MicrosoftUEFICertificateAuthority&quot;;

$vmProcessorCount = 4;
$vmMemoryStartUpBytes = 1GB;
$vmMemoryMinimumBytes =  500MB;
$vmMemoryMaximumBytes =  3GB;
$vmDynamicMemoryEnabled = $true;

New-VM -Name $vmName -BootDevice $vmBootDevice -NewVHDPath $vmNewDiskPath -Path $vmPath -NewVHDSizeBytes $vmNewDiskSize -Generation $vmGeneration -SwitchName $vmSwitchName
Set-VMFirmware $vmName -EnableSecureBoot $vmFirmwareEnableSecureBoot -SecureBootTemplate $vmFirmwareSecureBootTemplate
Set-VMProcessor $vmName -Count $vmProcessorCount
Set-VMMemory $vmName -DynamicMemoryEnabled $vmDynamicMemoryEnabled -MinimumBytes $vmMemoryMinimumBytes -StartupBytes $vmMemoryStartUpBytes -MaximumBytes $vmMemoryMaximumBytes
Add-VMDvdDrive $vmName -Path $vmDvdDrivePath # To eject run Remove-VMDvdDrive $vmName

</code></pre>
<h2 id="debugandcleanup">Debug and clean up</h2>
<pre><code class="language-powershell"># Debugging
Get-VMFirmware $vmName | fl
Get-VMProcessor $vmName | fl
Get-VMMemory $vmName | fl
Get-VMDVDDrive $vmName | fl 

# Clean up
Remove-VM -Name $vmName -Force
Remove-Item $vmNewDiskPath -Force
</code></pre>
<hr><h2 id="related-links">Related links</h2><p><a href="https://www.ubuntu.com/download/server">https://www.ubuntu.com/download/server</a></p><p><a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows">https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows</a><br><a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-ubuntu-virtual-machines-on-hyper-v">https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-ubuntu-virtual-machines-on-hyper-v</a><br><a href="https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/what-s-new-in-hyper-v-on-windows#BKMK_linux">https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/what-s-new-in-hyper-v-on-windows#BKMK_linux</a></p>]]></content:encoded></item><item><title><![CDATA[Setting up an OpenVPN server to access censored content in China]]></title><description><![CDATA[<h3 id="censorship-is-real-in-china-here-are-some-of-the-workarounds-i-can-think-of">Censorship is real in China. Here are some of the workarounds I can think of</h3><ul><li>Spin up a Windows server with GUI in Hong Kong or Japan then just remote deskop into the instance.</li><li>Spin up a Linux server in Hong Kong or Japan and setup a VPN and connect</li></ul>]]></description><link>https://blog.frankfu.com.au/2019/01/21/setting-up-an-openvpn-server-to-access-censored-content-in-china/</link><guid isPermaLink="false">5c597e78e2610f0001454586</guid><category><![CDATA[vpn]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Mon, 21 Jan 2019 07:29:51 GMT</pubDate><content:encoded><![CDATA[<h3 id="censorship-is-real-in-china-here-are-some-of-the-workarounds-i-can-think-of">Censorship is real in China. Here are some of the workarounds I can think of</h3><ul><li>Spin up a Windows server with GUI in Hong Kong or Japan then just remote deskop into the instance.</li><li>Spin up a Linux server in Hong Kong or Japan and setup a VPN and connect to this VPN using a Connect VPN client.</li><li>Purchase a VPN that China doesn't actively block. <a href="https://www.privateinternetaccess.com/">PIA </a>for example gets actively blocked by the great firewall where as a service like <a href="https://www.expressvpn.com/">ExpressVPN </a>works.</li><li>Purchase a data sim from outside China which is usable in China. <a href="https://simsdirect.com.au/">Sims Direct</a> , an Australian company, provides an data only sim which lets you use it in <a href="https://simsdirect.com.au/collections/asia/products/asia-4gb-data-data-travel-sim-card">18 asian countries for a span of 15 days</a></li></ul><p>I personally prefer the second method, because this means I can then download Open VPN Client on all my devices (iPad, iPhone, Laptop etc). </p><p>Spinning up a Linux box with <a href="https://openvpn.net/quick-start-guide/">OpenVPN </a>Access Server either pre-installed or self installation, configure the wizard, add desired users and permissions then lastly login as the desired user and download the  <em>Connection Profile</em> in the <code>.ovpn</code> format by logging in <a href="https://openvpnasip:943/">https://&lt;openvpnasip&gt;:943</a> and clicking the links in "Connection profiles can be downloaded for" section. You might have open the <code>.ovpn</code> file and find and replace any occurance of local to public IP addresses.</p>]]></content:encoded></item><item><title><![CDATA[Using Git via SSH on Windows 10 (1803) on Powershell]]></title><description><![CDATA[<p>Recently did a fresh install of Windows 10 with all the vital updates and tried to use <code>git fetch</code>  on both <code>cmd</code> and <code>powershell</code> but was prompted to enter my ssh key so I tried to run <code>ssh-add</code> but I got an error saying <code>Error connecting to agent: No such</code></p>]]></description><link>https://blog.frankfu.com.au/2018/12/15/ssh-on-windows-10-1803/</link><guid isPermaLink="false">5c597e78e2610f0001454585</guid><category><![CDATA[windows]]></category><category><![CDATA[git]]></category><category><![CDATA[ssh]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Sat, 15 Dec 2018 11:04:57 GMT</pubDate><content:encoded><![CDATA[<p>Recently did a fresh install of Windows 10 with all the vital updates and tried to use <code>git fetch</code>  on both <code>cmd</code> and <code>powershell</code> but was prompted to enter my ssh key so I tried to run <code>ssh-add</code> but I got an error saying <code>Error connecting to agent: No such file or directory</code> so then I tried <code>ssh-agent</code> and got an error saying <code>unable to start ssh-agent service, error :1058</code>  so what could it be?</p><p>A quick scan of <code>Get-Service ssh-agent | select *</code> showed the following</p><pre><code class="language-powershell">UserName            : LocalSystem
Description         : Agent to hold private keys used for public key authentication.
DelayedAutoStart    : False
BinaryPathName      : C:\Windows\System32\OpenSSH\ssh-agent.exe
StartupType         : Disabled
Name                : ssh-agent
RequiredServices    : {}
CanPauseAndContinue : False
CanShutdown         : False
CanStop             : False
DisplayName         : OpenSSH Authentication Agent
DependentServices   : {}
MachineName         : .
ServiceName         : ssh-agent
ServicesDependedOn  : {}
StartType           : Disabled
ServiceHandle       : Microsoft.Win32.SafeHandles.SafeServiceHandle
Status              : Stopped
ServiceType         : Win32OwnProcess
Site                :
Container           :
</code></pre>
<p>I quickly noticed that the <code>StartType</code> was set to <code>Disabled</code>. Not sure for what reason?  To fix this I had to first change it back to <code>Manual</code> then start the service again.</p><pre><code class="language-powershell">Get-Service -Name ssh-agent | Set-Service -StartupType Manual
Start-Service ssh-agent
</code></pre>
<p>But it didn't end here, I also had to instruct git to look for ssh in Windows' directory</p><pre><code class="language-bash">git config --global core.sshCommand C:/Windows/System32/OpenSSH/ssh.exe
</code></pre>
<p>So now that git knows where to look for ssh we can now add our private key to the ssh authentiction agent <code>ssh-add</code> then double checking with <code>ssh-add -l</code></p><pre><code>2048 SHA256:p80/dPfeSzXZPM7ba61214oXCNzMB+v+s/K8gexampleaWzx7Y /home/owo/.ssh/id_rsa (RSA)</code></pre>]]></content:encoded></item><item><title><![CDATA[Modifying host headers with Azure websites when using it behind an Application Gateway or reverse proxy via URL Rewrite Module]]></title><description><![CDATA[<p>I'm currently using <a href="https://docs.microsoft.com/en-us/azure/application-gateway/overview">Azure's Application Gateway</a> with a backend pool utilising <a href="https://azure.microsoft.com/en-au/services/app-service/">Azure's App Service</a>. When the application gateway forwards your request to the backpool, it also forwards <code>X-Original-Host</code> HTTP Header together with it to help you identity which listener the gateway originally acted upon but what if you wanted it</p>]]></description><link>https://blog.frankfu.com.au/2018/11/13/correcting-host-headers-with-azure-websites-when-using-it-behind-an-application-gateway-or-reverse-proxy/</link><guid isPermaLink="false">5c597e78e2610f000145457e</guid><category><![CDATA[azure]]></category><category><![CDATA[iis]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Tue, 13 Nov 2018 07:45:45 GMT</pubDate><content:encoded><![CDATA[<p>I'm currently using <a href="https://docs.microsoft.com/en-us/azure/application-gateway/overview">Azure's Application Gateway</a> with a backend pool utilising <a href="https://azure.microsoft.com/en-au/services/app-service/">Azure's App Service</a>. When the application gateway forwards your request to the backpool, it also forwards <code>X-Original-Host</code> HTTP Header together with it to help you identity which listener the gateway originally acted upon but what if you wanted it to match the <code>HOST</code> header at the web server (app service)? This might be useful for cases where you need to generate absolute URLs in your web app for whatever reason be it sitemaps or some syndication feed etc. Since we are using Azure's App service, we don't have as much control over IIS as we are used to but luckily for us Azure has provided us with a way to transform the <code>applicationHost.config</code> via transforms.</p><p>On the Azure Portal head over to your app service then jump over to the <code>Kudu &gt; Debug console &gt; CMD</code> and create a file name <code>applicationHost.xdt</code> under <code>d:\home\site</code> directory which allows you set the allowed server variables for IIS by overriding/transforming the <a href="https://docs.microsoft.com/en-us/iis/get-started/planning-your-iis-architecture/introduction-to-applicationhostconfig">applicationHost.config</a> file</p><p><strong>applicationHost.xdt</strong></p><pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"&gt;
  &lt;system.webServer&gt;
    &lt;rewrite&gt;
      &lt;allowedServerVariables xdt:Transform="Replace"&gt;
        &lt;add name="HTTP_X_ORIGINAL_HOST" xdt:Transform="InsertIfMissing" /&gt;
        &lt;add name="HTTP_HOST" xdt:Transform="InsertIfMissing" /&gt;
      &lt;/allowedServerVariables&gt;
    &lt;/rewrite&gt;
  &lt;/system.webServer&gt;
&lt;/configuration&gt;
</code></pre><p>Then on <code>Web.config</code> file within <code>d:\home\site\wwwroot</code> you'll need a new rule which basically sets the <code>HTTP_HOST</code> server variable to be <code>{HTTP_X_ORIGINAL_HOST}</code> (surrounding curly brace means to evaluate) on the condition that the value of the http header <code>X-Original-Host</code> is NOT empty (e.g. <code>^$</code>).</p><p><code>X-Original-Host</code> is a <a href="https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-faq">HTTP header which Azure's Application Gateway forwards</a> to its backend pools</p><p><strong>Web.config</strong></p><pre><code>&lt;configuration&gt; 
  &lt;system.webServer&gt;
    &lt;rewrite&gt;
      &lt;rules&gt;
        &lt;rule name="Set HOST header based on X-Original-Host header"&gt;
          &lt;match url="(.*)"&gt;&lt;/match&gt;
          &lt;conditions logicalGrouping="MatchAll" trackAllCaptures="false"&gt;
            &lt;add input="{HTTP_X_ORIGINAL_HOST}" pattern="^$" negate="true" /&gt;
          &lt;/conditions&gt;
          &lt;serverVariables&gt;
              &lt;set name="HTTP_HOST" value="{HTTP_X_ORIGINAL_HOST}"&gt;&lt;/set&gt;
          &lt;/serverVariables&gt;
        &lt;/rule&gt;
      &lt;/rules&gt;      
     &lt;/rewrite&gt;
  &lt;/system.webServer&gt;
&lt;/configuration&gt;
</code></pre><p><strong>IMPORTANT</strong> : Once the two changes are made, we will need to restart <strong>BOTH</strong> the SCM site (via the <code>Restart Site</code> button on {foo}.scm.azurewebsites.net/SiteExtensions) and also the website itself (Via the Azure Portal, CLI or Powershell) or the changes won't kick in.</p><p>And to test whether the correct headers are being transformed, simply add some debugging code in your html (or razor page in my case of ASP.NET MVC)</p><p><strong>Index.cshtml</strong></p><pre><code>&lt;div class="jumbotron"&gt;    
    &lt;p&gt;HTTP_HOST : @Request.ServerVariables["HTTP_HOST"]&lt;/p&gt;
    &lt;p&gt;HTTP_X_ORIGINAL_HOST : @Request.ServerVariables["HTTP_X_ORIGINAL_HOST"]&lt;/p&gt;   
&lt;/div&gt;

&lt;div class="jumbotron"&gt;
    &lt;p&gt;X-Forwarded-For : @Request.Headers["X-Forwarded-For"]&lt;/p&gt;    
    &lt;p&gt;X-Forwarded-Proto : @Request.Headers["X-Forwarded-Proto"]&lt;/p&gt;
    &lt;p&gt;X-Original-Host : @Request.Headers["X-Original-Host"]&lt;/p&gt;    
 &lt;/div&gt;
</code></pre><h3 id="side-note-for-deployments-outside-of-app-service">Side note for deployments outside of App Service</h3><p>If we have greater control over the hosting environment, perhaps using <a href="https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-windows-container-support-in-azure-app-service/">App Service for Windows Containers</a> (in Public preview at time of writing) the same could be achieved using powershell with <code>Set-WebConfigurationProperty</code> cmdlet in your <code>Dockerfile</code> while building your image. The below example assumes you've built your image using the <code>Default Web Site</code> but can obviously be changed to however your website is configured.</p><pre><code class="language-powershell">Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -location 'Default Web Site' ` 
-filter 'system.webServer/rewrite/allowedServerVariables' -Name '.'  `
-value ( @{ name = 'HTTP_HOST' }, @{ name = 'HTTP_X_ORIGINAL_HOST' } )
</code></pre>
<p>Which would produce</p><p><strong>applicationHost.config</strong></p><pre><code>&lt;configuration&gt;
    &lt;location path="Default Web Site" overrideMode="Allow"&gt;
            &lt;system.webServer&gt;
                &lt;rewrite&gt;
                    &lt;allowedServerVariables&gt;
                        &lt;add name="HTTP_X_ORIGINAL_HOST" /&gt;
                        &lt;add name="HTTP_HOST"/&gt;
                    &lt;/allowedServerVariables&gt;
                &lt;/rewrite&gt;
                &lt;asp /&gt;
            &lt;/system.webServer&gt;
    &lt;/location&gt;
&lt;/configuration&gt;</code></pre><p>Here is the <code>Dockerfile</code> for completeness</p>
<p><strong>Dockerfile</strong></p>
<pre><code class="language-docker">
ARG LTSC_VERSION=ltsc2016
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-${LTSC_VERSION}

RUN iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
RUN choco install urlrewrite -y

WORKDIR /inetpub/wwwroot 

RUN Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -location 'Default Web Site' -filter 'system.webServer/rewrite/allowedServerVariables' -Name '.'  -value ( @{ name = 'HTTP_HOST' }, @{ name = 'HTTP_X_ORIGINAL_HOST' } )

ARG PUBLISH_DIR
ADD output/${PUBLISH_DIR}/. ./

</code></pre>
<h3 id="references">References</h3><p><a href="https://github.com/projectkudu/kudu/wiki/Azure-Site-Extensions#understanding-what-could-go-wrong-with-xdt-transforms">https://github.com/projectkudu/kudu/wiki/Azure-Site-Extensions#understanding-what-could-go-wrong-with-xdt-transforms</a></p>]]></content:encoded></item><item><title><![CDATA[Creating a self signed certificate for web development on IIS using powershell]]></title><description><![CDATA[<p>We will need to run the following on Powershell with <strong>Administrator</strong> rights.</p>
<pre><code class="language-powershell"># Setup variables
$companyName = &quot;contoso&quot;
$certFilePath = &quot;$PSScriptRoot\$companyName-wildcard.pfx&quot;
$dnsName = &quot;*.$companyName.com&quot;
$date = (Get-Date).ToString('MMM-yyyy')
$certFriendlyname = &quot;$companyName-wildcard-$date&quot;
$certExpiry = (Get-Date).AddYears(10)

# Removal existing certificates
gci Cert:\LocalMachine\Root | Where</code></pre>]]></description><link>https://blog.frankfu.com.au/2018/06/20/creating-self-signed-certificates-for-web-development-on-iis/</link><guid isPermaLink="false">5c597e78e2610f000145457c</guid><category><![CDATA[powershell]]></category><category><![CDATA[iis]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Wed, 20 Jun 2018 06:41:13 GMT</pubDate><content:encoded><![CDATA[<p>We will need to run the following on Powershell with <strong>Administrator</strong> rights.</p>
<pre><code class="language-powershell"># Setup variables
$companyName = &quot;contoso&quot;
$certFilePath = &quot;$PSScriptRoot\$companyName-wildcard.pfx&quot;
$dnsName = &quot;*.$companyName.com&quot;
$date = (Get-Date).ToString('MMM-yyyy')
$certFriendlyname = &quot;$companyName-wildcard-$date&quot;
$certExpiry = (Get-Date).AddYears(10)

# Removal existing certificates
gci Cert:\LocalMachine\Root | Where FriendlyName -Like &quot;*$companyName*&quot; | Remove-Item
gci Cert:\LocalMachine\My | Where FriendlyName -Like &quot;*$companyName*&quot; | Remove-Item

# Create self signed certificate and store thumbprint variable
$thumb = (New-SelfSignedCertificate -DnsName $dnsName -CertStoreLocation cert:\LocalMachine\My -FriendlyName $certFriendlyname -NotAfter $certExpiry).Thumbprint

# Export self signed certificate to local file system with mandatory password
$pwd = ConvertTo-SecureString -String &quot;YourPassword&quot; -Force -AsPlainText
Export-PfxCertificate -Cert cert:\LocalMachine\My\$thumb -FilePath $certFilePath -Password $pwd

# Adding the exported certificate into the Trusted Root Certificate Authorities store
Import-PfxCertificate -CertStoreLocation Cert:\LocalMachine\Root\ -FilePath $certFilePath -Password $pwd

# Rebinding existing ssl certificates in case Windows decides to remove third party certificates from Root
$bindings = Get-WebBinding | Where { $_.Protocol -eq &quot;https&quot; -and $_.bindingInformation -like &quot;*$companyName*&quot; }
$cert = gci Cert:\LocalMachine\MY | Where Subject -Like *$companyName* | select -First 1
foreach ($b in $bindings)
{
    $b.RebindSslCertificate($cert.GetCertHashString(), &quot;My&quot;) | Out-Null
}
</code></pre>
<p>We can verify that all this has been done correctly by heading over to <code>Windows</code> &gt; <code>run</code> &gt; <code>certlm.msc</code></p>
<ul>
<li><code>Personal</code> &gt; <code>Certificates</code> and</li>
<li><code>Personal</code> &gt; <code>Trusted Root Certification</code></li>
</ul>
<p>And then check for the entry with &quot;Issued By&quot; as <code>*.contoso.com</code></p>
<p>Then we will need to add an entry to override DNS by editing our host file on<br>
<code>c:\Windows\System32\Drivers\etc\hosts</code>.</p>
<pre><code>127.0.0.1     test.contoso.com
</code></pre>
<p>Once all this is done, we can fire up IIS Manager (<code>inetmgr</code>) and add a binding to the website of our choice with the newly generated certificate.</p>
<p>Check <a href="https://test.contoso.com">https://test.contoso.com</a> on your favourite browser and make sure the location bar is green.</p>
<h2 id="reversalforeverythingabove">Reversal for everything above</h2>
<pre><code class="language-powershell">Remove-Item Cert:\LocalMachine\My\$thumb
Remove-Item Cert:\LocalMachine\AuthRoot\$thumb
Remove-Item C:\contoso-wildcard.pfx
</code></pre>
<h2 id="importantnotes">Important notes</h2>
<p>It appears that sometimes these certificates get removed from the Root Store which results in errors like</p>
<blockquote>
<p>This server could not prove that it is test.contoso.com; its security certificate is not trusted by your computer's operating system. This may be caused by a misconfiguration or an attacker intercepting your connection.</p>
</blockquote>
<p>It turns out that Windows has the ability to remove certs. Still need to figure out why and how we can stop this from happening.</p>
<p>These links could be related</p>
<p><a href="https://serverfault.com/questions/639280/trusted-root-certificate-being-automatically-removed-from-store">https://serverfault.com/questions/639280/trusted-root-certificate-being-automatically-removed-from-store</a><br>
<a href="https://serverfault.com/questions/752146/why-are-many-admins-using-turn-off-automatic-root-certificates-update-policy">https://serverfault.com/questions/752146/why-are-many-admins-using-turn-off-automatic-root-certificates-update-policy</a><br>
<a href="https://serverfault.com/questions/526736/preventing-windows-from-deleteing-our-root-ca">https://serverfault.com/questions/526736/preventing-windows-from-deleteing-our-root-ca</a><br>
<a href="https://superuser.com/questions/217719/what-are-the-windows-system-certificate-stores">https://superuser.com/questions/217719/what-are-the-windows-system-certificate-stores</a></p>
]]></content:encoded></item><item><title><![CDATA[Automate start/stop of NiceHash Miner]]></title><description><![CDATA[<h3 id="motivation">Motivation</h3>
<p>NiceHash Miner is an incredibly simple program to use and I've been wanting to start and stop the program using the <em>Windows Task Scheduler</em>. I initially tried <strong>stop</strong> the task by using the <strong>&quot;Stop task if running more than x&quot;</strong> option in the settings section but quickly</p>]]></description><link>https://blog.frankfu.com.au/2018/05/02/automate-start-stop-of-nicehash-miner/</link><guid isPermaLink="false">5c597e78e2610f000145457b</guid><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Wed, 02 May 2018 04:13:10 GMT</pubDate><content:encoded><![CDATA[<h3 id="motivation">Motivation</h3>
<p>NiceHash Miner is an incredibly simple program to use and I've been wanting to start and stop the program using the <em>Windows Task Scheduler</em>. I initially tried <strong>stop</strong> the task by using the <strong>&quot;Stop task if running more than x&quot;</strong> option in the settings section but quickly found that <code>NiceHash Miner 2.exe</code> actually launches another process named <code>excavator.exe</code> (and not as a child process) which doesn't get stopped even though <code>NiceHash Miner 2.exe</code> was stopped by the <em>Windows Task Scheduler</em>.</p>
<p>The work around was to create a helper in the form of a powershell script to start and stop the individual processes. You'll see the end result below.</p>
<p><strong>Note : You'll also need to enable the &quot;Autostart mining&quot; within NiceHash Miner for this to work.</strong></p>
<p><img src="https://blog.frankfu.com.au/content/images/2018/05/p46.PNG" alt="p46"><br>
<img src="https://blog.frankfu.com.au/content/images/2018/05/p47.PNG" alt="p47"></p>
<hr>
<h3 id="setexecutionpolicytoremotesigned">Set execution policy to remote signed</h3>
<pre><code class="language-powershell">Set-ExecutionPolicy RemoteSigned
</code></pre>
<h3 id="creatingapowershellscripttostartstopnicehashminerprocesses">Creating a powershell script to start/stop NiceHash Miner processes</h3>
<p><code>NiceHashHelper.ps1</code></p>
<pre><code class="language-powershell"># usage : e.g. &quot;powershell .\NiceHashHelper.ps1 -action start&quot; 

param ([string]$action = &quot;Start&quot;)

if($action -eq &quot;Start&quot;)
{
    Write-Host &quot;Starting..&quot;
    Start-Process &quot;$env:LOCALAPPDATA\Programs\NiceHash Miner 2\NiceHash Miner 2.exe&quot;
} 
elseif ($action -eq &quot;Stop&quot;)
{
    Write-Host &quot;Stopping..&quot;
    Stop-Process -Name *NiceHash*            # The main program
    Stop-Process -Name *excavator*           # The GPU miner
    Stop-Process -Name *xmr-stak-cpu*        # The CPU miner
}
else {
    Write-Host &quot;Invalid action : Please choose either Start or Stop with the action param&quot;
}
</code></pre>
<h3 id="creatingataskintaskscheduler">Creating a task in Task Scheduler</h3>
<pre><code class="language-powershell"># Variables
$cmd = &quot;$env:USERPROFILE\Desktop\NiceHash\NiceHashHelper.ps1&quot;
$argumentPrefix =  '-command &quot;&amp; {0} -action &quot;' -f ($cmd)
$startTrigger =  New-ScheduledTaskTrigger -Daily -At 6pm
$stopTrigger =  New-ScheduledTaskTrigger -Daily -At 7am
$taskFolderName = '\NiceHash Tasks'
$executable = &quot;Powershell.exe&quot;

# The &quot;Start&quot; task
$startActionArgument = '{0} {1}' -f ($argumentPrefix, &quot;Start&quot;)
$startAction = New-ScheduledTaskAction -Execute $executable -Argument $startActionArgument
Register-ScheduledTask -Action $startAction -Trigger $startTrigger -TaskName &quot;Start NiceHash Miner&quot; -TaskPath $taskFolderName

# The &quot;Stop&quot; task
$stopActionArgument = '{0} {1}' -f ($argumentPrefix, &quot;Stop&quot;)
$stopAction = New-ScheduledTaskAction -Execute $executable -Argument $stopActionArgument
Register-ScheduledTask -Action $stopAction -Trigger $stopTrigger -TaskName &quot;Stop NiceHash Miner&quot; -TaskPath $taskFolderName

# Unregister-ScheduledTask -TaskName &quot;Start NiceHash Miner&quot;
# Unregister-ScheduledTask -TaskName &quot;Stop NiceHash Miner&quot;
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Checking for database deadlocks on Azure SQL]]></title><description><![CDATA[<p>Recently, we've been trialing <a href="https://docs.microsoft.com/en-us/azure/sql-database/sql-database-sync-data">Azure's SQL Data Sync</a> (in Preview at time of writing) feature which allows you to sync database schema and data between multiple databases. This has been working great but we've run into some issues with database triggers so we wanted to dig down deeper.</p>
<h3 id="checkingforirregularities">Checking for</h3>]]></description><link>https://blog.frankfu.com.au/2018/03/19/checking-for-database-deadlocks-on-sql-server/</link><guid isPermaLink="false">5c597e78e2610f000145457a</guid><category><![CDATA[azure-sql]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Mon, 19 Mar 2018 01:02:30 GMT</pubDate><content:encoded><![CDATA[<p>Recently, we've been trialing <a href="https://docs.microsoft.com/en-us/azure/sql-database/sql-database-sync-data">Azure's SQL Data Sync</a> (in Preview at time of writing) feature which allows you to sync database schema and data between multiple databases. This has been working great but we've run into some issues with database triggers so we wanted to dig down deeper.</p>
<h3 id="checkingforirregularities">Checking for irregularities</h3>
<pre><code class="language-sql">use master

-- check for recent events
select top 1000 * from sys.event_log order by start_time desc

-- filter by severity
select top 100 * from sys.event_log where severity &gt; 0 order by start_time desc
</code></pre>
<h3 id="toviewmoredetailsaboutthedeadlocks">To view more details about the dead locks</h3>
<pre><code class="language-sql">use master

WITH CTE AS (

       SELECT CAST(event_data AS XML)  AS [target_data_XML]

       FROM sys.fn_xe_telemetry_blob_target_read_file('dl', null, null, null)

)

SELECT target_data_XML.value('(/event/@timestamp)[1]', 'DateTime2') AS Timestamp,

target_data_XML.query('/event/data[@name=''xml_report'']/value/deadlock') AS deadlock_xml,

target_data_XML.query('/event/data[@name=''database_name'']/value').value('(/value)[1]', 'nvarchar(100)') AS db_name

FROM CTE
</code></pre>
<h3 id="references">References</h3>
<p><a href="https://blogs.msdn.microsoft.com/azuresqldbsupport/2017/01/21/lesson-learned-19-how-to-obtain-the-deadlocks-of-your-azure-sql-database/">https://blogs.msdn.microsoft.com/azuresqldbsupport/2017/01/21/lesson-learned-19-how-to-obtain-the-deadlocks-of-your-azure-sql-database/</a></p>
]]></content:encoded></item><item><title><![CDATA[Finding and tailing IIS HTTP logs]]></title><description><![CDATA[<p>Find where the logs are located for each website</p>
<pre><code class="language-powershell">Import-Module WebAdministration
foreach($WebSite in $(get-website))
{
    $logFile=&quot;$($Website.logFile.directory)\w3scv$($website.id)&quot;.replace(&quot;%SystemDrive%&quot;,$env:SystemDrive)
    Write-host &quot;$($WebSite.name) [$logfile]&quot;
}
</code></pre>
<pre><code class="language-powershell"># Results
mysite1.dev[C:\inetpub\logs\LogFiles\w3scv2]
mysite2.dev[C:\inetpub\logs\LogFiles\</code></pre>]]></description><link>https://blog.frankfu.com.au/2018/03/16/finding-and-tailing-iis-http-logs/</link><guid isPermaLink="false">5c597e78e2610f0001454579</guid><category><![CDATA[iis]]></category><category><![CDATA[powershell]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Fri, 16 Mar 2018 01:21:05 GMT</pubDate><content:encoded><![CDATA[<p>Find where the logs are located for each website</p>
<pre><code class="language-powershell">Import-Module WebAdministration
foreach($WebSite in $(get-website))
{
    $logFile=&quot;$($Website.logFile.directory)\w3scv$($website.id)&quot;.replace(&quot;%SystemDrive%&quot;,$env:SystemDrive)
    Write-host &quot;$($WebSite.name) [$logfile]&quot;
}
</code></pre>
<pre><code class="language-powershell"># Results
mysite1.dev[C:\inetpub\logs\LogFiles\w3scv2]
mysite2.dev[C:\inetpub\logs\LogFiles\w3scv3]
mysite3.dev[C:\inetpub\logs\LogFiles\w3scv4]
mysite4.dev[C:\inetpub\logs\LogFiles\w3scv5]
mysite5.dev[C:\inetpub\logs\LogFiles\w3scv1]
mysite6.dev[C:\inetpub\logs\LogFiles\w3scv6]
</code></pre>
<pre><code class="language-powershell"># Let's tail the last 5 rows mysite1's log and wait for more
gc C:\inetpub\logs\LogFiles\w3scv2\u_ex180316.log -tail 5 -wait
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Changing the Network Profile using Powershell]]></title><description><![CDATA[<p>Recently wanted to enable remote management of a server via powershell so that I could try out <a href="https://docs.microsoft.com/en-us/windows-server/manage/honolulu/honolulu">Project Honolulu</a> for managing Windows Servers</p>
<p>Make sure <code>WinRM</code> service is running and start up to automatic</p>
<pre><code class="language-powershell">Get-Service winrm
</code></pre>
<p>Enable Powershell Remoting</p>
<pre><code class="language-powershell">Enable-PSRemoting -Force
</code></pre>
<p>Ensure network profiles are ok</p>
<pre><code class="language-powershell"># Show network profile
&gt;</code></pre>]]></description><link>https://blog.frankfu.com.au/2018/03/14/changing-the-network-profile-using-powershell/</link><guid isPermaLink="false">5c597e78e2610f0001454578</guid><category><![CDATA[powershell]]></category><category><![CDATA[networking]]></category><category><![CDATA[windows-server]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Wed, 14 Mar 2018 06:36:46 GMT</pubDate><content:encoded><![CDATA[<p>Recently wanted to enable remote management of a server via powershell so that I could try out <a href="https://docs.microsoft.com/en-us/windows-server/manage/honolulu/honolulu">Project Honolulu</a> for managing Windows Servers</p>
<p>Make sure <code>WinRM</code> service is running and start up to automatic</p>
<pre><code class="language-powershell">Get-Service winrm
</code></pre>
<p>Enable Powershell Remoting</p>
<pre><code class="language-powershell">Enable-PSRemoting -Force
</code></pre>
<p>Ensure network profiles are ok</p>
<pre><code class="language-powershell"># Show network profile
&gt; Get-NetConnectionProfile

Name             : Network
InterfaceAlias   : Ethernet
InterfaceIndex   : 2
NetworkCategory  : Public
IPv4Connectivity : Internet
IPv6Connectivity : NoTraffic

# Set network profile from Public to Private
&gt; Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private

# Verify the update completed
&gt; Get-NetConnectionProfile

Name             : Network
InterfaceAlias   : Ethernet
InterfaceIndex   : 2
NetworkCategory  : Private
IPv4Connectivity : Internet
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Creating a headless Windows Server 2016 VM for CI/CD using VSTS]]></title><description><![CDATA[<h2 id="settingupvisualstudiobuildtools2017">Setting up Visual Studio Build Tools 2017</h2>
<p>Recently wanted to setup a <a href="https://docs.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core">Windows Server Core</a> VM/image for portability. It's mainly used to build ASP.NET MVC web apps on the full .NET Framework which would then need to be deploy to Azure. Just documenting the steps I took along</p>]]></description><link>https://blog.frankfu.com.au/2018/03/13/creating-a-headless-windows-server-2016-vm-for-ci-cd-using-vsts/</link><guid isPermaLink="false">5c597e78e2610f0001454577</guid><category><![CDATA[vsts]]></category><category><![CDATA[windows-server]]></category><category><![CDATA[powershell]]></category><category><![CDATA[msbuild-tools]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Tue, 13 Mar 2018 15:04:53 GMT</pubDate><content:encoded><![CDATA[<h2 id="settingupvisualstudiobuildtools2017">Setting up Visual Studio Build Tools 2017</h2>
<p>Recently wanted to setup a <a href="https://docs.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core">Windows Server Core</a> VM/image for portability. It's mainly used to build ASP.NET MVC web apps on the full .NET Framework which would then need to be deploy to Azure. Just documenting the steps I took along the way.</p>
<pre><code class="language-powershell"># Download and install VS Build Tools
Invoke-WebRequest https://aka.ms/vs/15/release/vs_buildtools.exe -OutFile vs_buildtools.exe ;
Start-Process -FilePath 'vs_BuildTools.exe' -ArgumentList `
  '--quiet', '--norestart', '--locale en-US' `
, '--add Microsoft.VisualStudio.Workload.NetCoreBuildTools' `
, '--add Microsoft.VisualStudio.Workload.WebBuildTools' `
, '--includeOptional', '--includeRecommended' -Wait ;
</code></pre>
<h3 id="references">References</h3>
<p><a href="https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools">Other Workloads</a><br>
<a href="https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio">Installer CLI Docs</a><br>
<a href="https://docs.microsoft.com/en-us/visualstudio/install/command-line-parameter-examples">Installer CLI Examples</a></p>
<h3 id="setupinstallers">Setup Installers</h3>
<pre><code class="language-powershell"># Install chocolatey
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

# Choco setup
choco feature enable -n allowGlobalConfirmation
</code></pre>
<h2 id="settingupotherdependencies">Setting up other dependencies</h2>
<p>My builds require Web Deploy and NodeJS so let's install them using <a href="https://chocolatey.org/">Chocolatey</a></p>
<pre><code>choco install git 
choco install webdeploy
choco install nodejs-lts
choco install nuget.commandline
</code></pre>
<h2 id="nextsteps">Next steps</h2>
<p>Would like to containerize this image with exposable environment variables such as VSTS URLs, PAT etc.</p>
]]></content:encoded></item><item><title><![CDATA[Enabling Network Discovery and File and Printer sharing on Windows Server]]></title><description><![CDATA[<h2 id="warningthisdoesnotworkwindowsservercore">WARNING : This does not work Windows Server Core</h2>
<p>Some folk have been arriving to this post hoping to find a solution for Windows Server Core might need to look elsewhere. I've only tested this on Windows Server 2012.</p>
<h2 id="servicerequirements">Service requirements</h2>
<p>We need to make sure the following services are running</p>]]></description><link>https://blog.frankfu.com.au/2018/03/12/enabling-network-discovery-on-windows-server/</link><guid isPermaLink="false">5c597e78e2610f0001454576</guid><category><![CDATA[windows-server]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Mon, 12 Mar 2018 14:19:44 GMT</pubDate><content:encoded><![CDATA[<h2 id="warningthisdoesnotworkwindowsservercore">WARNING : This does not work Windows Server Core</h2>
<p>Some folk have been arriving to this post hoping to find a solution for Windows Server Core might need to look elsewhere. I've only tested this on Windows Server 2012.</p>
<h2 id="servicerequirements">Service requirements</h2>
<p>We need to make sure the following services are running</p>
<pre><code class="language-powershell"># Check status of services required for Network Discovery on Windows
Get-Service -DisplayName &quot;Function Discovery Resource Publication&quot;
Get-Service -DisplayName &quot;DNS Client&quot; 
Get-Service -DisplayName &quot;SSDP Discovery&quot; 
Get-Service -DisplayName &quot;UPnP Device Host&quot;
</code></pre>
<p>If not any are not running, start them</p>
<pre><code># Start required services
Get-Service -DisplayName &quot;Function Discovery Resource Publication&quot; | Start-Service
Get-Service -DisplayName &quot;DNS Client&quot;  | Start-Service
Get-Service -DisplayName &quot;SSDP Discovery&quot;  | Start-Service
Get-Service -DisplayName &quot;UPnP Device Host&quot; | Start-Service
</code></pre>
<h2 id="firewallrequirements">Firewall requirements</h2>
<p>Check whether respective firewall rules are both set to <strong>Allow</strong> and also <strong>Enabled</strong></p>
<pre><code class="language-powershell">Get-NetFirewallRule -DisplayGroup &quot;Network Discovery&quot; | ft 
Get-NetFirewallRule -DisplayGroup &quot;File and Printer Sharing&quot; | ft
</code></pre>
<p>If not, allow and enable them</p>
<pre><code class="language-powershell"># Set action to allow
Get-NetFirewallRule -DisplayGroup &quot;Network Discovery&quot; | Set-NetFirewallRule -Action Allow
# Enabling the rule
Get-NetFirewallRule -DisplayGroup &quot;Network Discovery&quot; | Enable-NetFirewallRule

# Set action to allow
Get-NetFirewallRule -DisplayGroup &quot;File and Printer Sharing&quot; | Set-NetFirewallRule -Action Allow 
# Enabling the rule
Get-NetFirewallRule -DisplayGroup &quot;File and Printer Sharing&quot; | Enable-NetFirewallRule
</code></pre>
<h2 id="references">References</h2>
<p><a href="https://support.microsoft.com/en-au/help/2722035/you-cannot-turn-on-network-discovery-in-network-and-sharing-center-in">https://support.microsoft.com/en-au/help/2722035/you-cannot-turn-on-network-discovery-in-network-and-sharing-center-in</a></p>
]]></content:encoded></item><item><title><![CDATA[Using Powershell's Invoke-WebRequest to persist state between requests using SessionVariable]]></title><description><![CDATA[<p>Recently, I've been testing ASP.NET MVC's Output Cache feature and wanted to test using <a href="https://msdn.microsoft.com/en-us/library/hdxfb6cy(v=vs.85).aspx">different parameters</a> such as <code>VaryByCustom</code> which would read a user's cookies and construct a cache key according to some value.</p>
<p>I ended up having to log in and out of Google Chrome, Firefox, Edge etc</p>]]></description><link>https://blog.frankfu.com.au/2018/03/12/using-powershells-invoke-webrequest-to-persist-state-between-request-using-sessionvariable/</link><guid isPermaLink="false">5c597e78e2610f0001454575</guid><category><![CDATA[powershell]]></category><dc:creator><![CDATA[Frank Fu]]></dc:creator><pubDate>Mon, 12 Mar 2018 07:37:12 GMT</pubDate><content:encoded><![CDATA[<p>Recently, I've been testing ASP.NET MVC's Output Cache feature and wanted to test using <a href="https://msdn.microsoft.com/en-us/library/hdxfb6cy(v=vs.85).aspx">different parameters</a> such as <code>VaryByCustom</code> which would read a user's cookies and construct a cache key according to some value.</p>
<p>I ended up having to log in and out of Google Chrome, Firefox, Edge etc just to test this so I created a script to do so instead.</p>
<pre><code>$loginUrl = &quot;https://example.com/log-in&quot;
$normalUrl = &quot;https://example.com/logged-in-area&quot;

$postParams = @{email='foo@bar.com';password='1234'}
iwr -Uri $url  -Method POST -Body $postParams -SessionVariable MySessionVariable
iwr $normalUrl -WebSession $MySessionVariable

# Optionally view what cookies got stored
$MySessionVariable.Cookies.GetCookies($url)
</code></pre>
<p>The magic lies in the <code>MySessionVariable</code> variable which is basically a Cookie object that gets passed in each invocation.</p>
]]></content:encoded></item></channel></rss>