<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>2020 on Funky Si's Blog (Dev)</title><link>https://blog-dev.funkysi1701.com/2020/</link><description>Recent content in 2020 on Funky Si's Blog (Dev)</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><managingEditor>funkysi1701@gmail.com (Simon Foster)</managingEditor><webMaster>funkysi1701@gmail.com (Simon Foster)</webMaster><lastBuildDate>Mon, 28 Dec 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://blog-dev.funkysi1701.com/2020/index.xml" rel="self" type="application/rss+xml"/><item><title>Set and achieve 2021 developer goals</title><link>https://blog-dev.funkysi1701.com/posts/2020/goals-for-2021/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 28 Dec 2020 00:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/goals-for-2021/</guid><category term="Goals">Goals</category><description>&lt;p&gt;I didn&amp;rsquo;t make any goals for 2020, or if I did, I didn&amp;rsquo;t officially announce them. 2020 has been a hard year for all of us, 2021 is going to be better.&lt;/p&gt;
&lt;p&gt;Here are a few ideas for my goals for the year ahead.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Azure certification&lt;/li&gt;
&lt;li&gt;Mentoring&lt;/li&gt;
&lt;li&gt;F#&lt;/li&gt;
&lt;li&gt;Cosmos db/Mongo db&lt;/li&gt;
&lt;li&gt;Give a talk&lt;/li&gt;
&lt;li&gt;Mandlebrot Generator&lt;/li&gt;
&lt;li&gt;Pwned Pass Mobile App&lt;/li&gt;
&lt;li&gt;More time for me&lt;/li&gt;
&lt;/ol&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I want to get a certificate to show how much I know. The obvious area for this is Azure. I spend a lot of time playing with Azure, building and deploying to the platform I should be able to get certified in this area. I actually had an exam booked in 2020 but it was an in-person exam so was cancelled when Covid hit. I plan to sit the &lt;a href="https://docs.microsoft.com/en-us/learn/certifications/exams/az-900" target="_blank" rel="noopener noreferrer"&gt;foundation&lt;/a&gt;
exam in the first quarter of 2021 and I will take it from there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I have had a few times recently where I have been reminded that I don&amp;rsquo;t have much experience working with others. Mostly because I work in a one-man development team so there are limited opportunities in the workplace. I have a few ideas to change this, but this is one of my top priorities for 2021.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;amp; 4) My experience is very Microsoft and C# so I want to spend some time exploring and learning tech that is adjacent to this. A functional language like F# sounds like a good compliment to my existing skills and my data skills are also very SQL server so some document database skills would be a good place to spend some time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Doing a talk has been on my list for years. I am terrified of doing one and with everything online now I don&amp;rsquo;t know if that makes things easier or harder. I don&amp;rsquo;t think 2021 is going to be the year for this but maybe I will take baby steps towards this goal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Twitter recently reminded me about watching a Mandlebrot generate one pixel at a time in the 1980s. I would love to explore the code used to generate them and how fast they currently are to produce.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My Google play store Xamarin forms app that uses the HIBP API has less than 100 users and I am considering closing it down, especially as I pay a monthly fee for API keys. I haven&amp;rsquo;t really decided what to do, maybe I will spend some time improving it, maybe I will close it down, maybe I will build something else.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For many people 2020 has been a hard year, I am one of those.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Building a Twitter Clone</title><link>https://blog-dev.funkysi1701.com/posts/2020/building-a-twitter-clone/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Tue, 22 Dec 2020 20:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/building-a-twitter-clone/</guid><category term="Twitter">Twitter</category><category term="Design">Design</category><category term="Architecture">Architecture</category><description>&lt;p&gt;I saw a tweet about building a twitter clone being harder than you would think. So this of course started me thinking how I would go about building something like that.&lt;/p&gt;
&lt;p&gt;Ok so where would I start? First a few assumptions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Development by a lone developer ie me&lt;/li&gt;
&lt;li&gt;Tech stack will be dotnet and other tech I am familiar with&lt;/li&gt;
&lt;li&gt;Database backend, probably SQL Server but I might use table storage for cost reasons should I try and actually build this. However if I design this well this should be something that could be swapped out as the system grows&lt;/li&gt;
&lt;li&gt;User accounts on the system will be small as I can&amp;rsquo;t imagine anyone ever signing up. Why sign up to a social media platform with no users?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="i-guess-the-next-question-is-what-is-twitter"&gt;I guess the next question is what is Twitter?&lt;a class="anchor ms-1" href="#i-guess-the-next-question-is-what-is-twitter" aria-label="Permalink: I guess the next question is what is Twitter?"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A website that allows you to share 280 characters of text with your followers, allows you to follow other users updates and allows other user to follow your updates.&lt;/p&gt;
&lt;p&gt;It also has an API that allow you to do almost everything that you can with the website.&lt;/p&gt;
&lt;p&gt;Then there are of course mobile apps to consider but I am going to assume this is out of scope, however assuming a good enough API then this shouldn&amp;rsquo;t be a problem for future development.&lt;/p&gt;
&lt;h2 id="first-steps"&gt;First Steps&lt;a class="anchor ms-1" href="#first-steps" aria-label="Permalink: First Steps"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To start off with I would concentrate on the API, and then build a web client that makes use of the API.&lt;/p&gt;
&lt;p&gt;So what would my MVP (minimum viable product) be?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User can authenticate with my API to get a token which allows access to other API endpoints&lt;/li&gt;
&lt;li&gt;User can create a tweet&lt;/li&gt;
&lt;li&gt;User can view own tweets&lt;/li&gt;
&lt;li&gt;User can view tweets of another user&lt;/li&gt;
&lt;li&gt;User can view tweets in their timeline&lt;/li&gt;
&lt;li&gt;User can follow/unfollow other users&lt;/li&gt;
&lt;li&gt;User can search for other users&lt;/li&gt;
&lt;li&gt;User can search for keywords in tweets&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think that is probably sufficient to build my MVP for the API.&lt;/p&gt;
&lt;p&gt;An interesting side note is that I could use the OAuth Twitter authentication to allow users to login to my twitter clone with the real twitter login details. However this makes no sense to me as we are essentially adding a dependency on the real twitter.&lt;/p&gt;
&lt;p&gt;So what would I use for the frontend? I would start off with a Client Side Blazor frontend. Once I had a proof of concept that worked, I would think about styling and adding the UI elements that are familiar to twitter users.&lt;/p&gt;
&lt;p&gt;The beauty of Client Side Blazor is that I can host cheaply in azure storage and distribute around the world via a CDN.&lt;/p&gt;
&lt;p&gt;Due to the high number of times that follower and following count and other stats are queried I would consider storing these in the database and include a regular job to recalculate them so they don&amp;rsquo;t get out of sync with the data.&lt;/p&gt;
&lt;p&gt;Having said all of this I am very tempted to fire up Visual Studio and see how far I get, and what problems I encounter along the way.&lt;/p&gt;</description></item><item><title>#005: Twitter Cards</title><link>https://blog-dev.funkysi1701.com/posts/2020/005-twitter-cards/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 12 Dec 2020 20:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/005-twitter-cards/</guid><category term="Twitter">Twitter</category><description>&lt;p&gt;It has been a bit of a mad week this week. I joined a new team so lots of time learning what&amp;rsquo;s what and also being pulled in two directions as usual demands come through on top of that.&lt;/p&gt;
&lt;p&gt;My blog runs on Blazor and I have been making use of JavaScript interop to update the html headers and update the page title to match the blog post article. This works great, I load the page and check the headers and they were saying what I wanted.&lt;/p&gt;
&lt;p&gt;The problem was I wanted to add tags for &lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/guides/getting-started" target="_blank" rel="noopener noreferrer"&gt;twitter cards&lt;/a&gt;
This means that when I paste a link to my blog on twitter you get a nice preview and pic of me in the tweet. This was not working at all even though I had the correct headers.&lt;/p&gt;
&lt;p&gt;I eventually figured out that the problem was the fact I was using JavaScript to update my headers after the page had been initially loaded. Twitter was fetching my page before these headers got added and therefore couldn&amp;rsquo;t see the twitter card headers.&lt;/p&gt;
&lt;p&gt;My solution was to use invalid html. Not ideal but it works. I added the required html tags in the body of my page using Blazor/C# instead of using JavaScript to add them into the header. Twitter appears to not be fussy in finding them in the wrong place.&lt;/p&gt;
&lt;p&gt;Twitter provides a validator tool at &lt;a href="https://cards-dev.twitter.com/validator" target="_blank" rel="noopener noreferrer"&gt;Twitter Card Validator&lt;/a&gt;
which my website now passes.&lt;/p&gt;
&lt;p&gt;Not much else to say this week, apart from I am missing Visual Studio and C#, I have been mostly using VS Code on Linux and looking at php which isn&amp;rsquo;t as much fun as my usual day job.&lt;/p&gt;</description></item><item><title>Weekly Update #004</title><link>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-004/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sun, 06 Dec 2020 20:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-004/</guid><category term="SQL">SQL</category><description>&lt;p&gt;I use sp_send_dbmail to send results of sql queries by email to business users. Recently an issue was raised that data was being cut off after 255 characters. To fix this I added @query_no_truncate = 1, however this stopped the column headings from being included. No idea why you can&amp;rsquo;t have all the data and column headings but there you have it.&lt;/p&gt;
&lt;p&gt;What I am doing now is running 2 queries, one to get the headings, and one to get the data. In theory you should be able to combine them with a Union however you then have datatype issues for non text columns so I gave up with that idea.&lt;/p&gt;
&lt;p&gt;My results have 60 something columns (don&amp;rsquo;t ask its for a data import into a third party system!) so I am not typing them all out. I can shove query results into a temporary table and then execute to get a list of columns.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; tempdb.sys.columns
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; object_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; object_id(&lt;span style="color:#e6db74"&gt;&amp;#39;tempdb..#TempTable&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However I need my list to be horizontal so I can use as column headers. I can use dynamic SQL and a pivot to flip them round.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;DECLARE&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;cols &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; NVARCHAR(&lt;span style="color:#66d9ef"&gt;MAX&lt;/span&gt;), &lt;span style="color:#f92672"&gt;@&lt;/span&gt;query &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; NVARCHAR(&lt;span style="color:#66d9ef"&gt;MAX&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;cols &lt;span style="color:#f92672"&gt;=&lt;/span&gt; STUFF((&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; QUOTENAME(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; tempdb.sys.columns
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; object_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; object_id(&lt;span style="color:#e6db74"&gt;&amp;#39;tempdb..#TempTable&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FOR&lt;/span&gt; XML PATH(&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;), &lt;span style="color:#66d9ef"&gt;TYPE&lt;/span&gt;).value(&lt;span style="color:#e6db74"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;NVARCHAR(MAX)&amp;#39;&lt;/span&gt;),&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SET&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT &amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;cols &lt;span style="color:#f92672"&gt;+&lt;/span&gt; N&lt;span style="color:#e6db74"&gt;&amp;#39; FROM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; SELECT name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; FROM tempdb.sys.columns
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; WHERE object_id = object_id(&amp;#39;&amp;#39;tempdb..#TempTable&amp;#39;&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;) x
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;PIVOT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; MAX(name)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; FOR name IN (&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;cols &lt;span style="color:#f92672"&gt;+&lt;/span&gt; N&lt;span style="color:#e6db74"&gt;&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;) y&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Weekly update 003: progress notes</title><link>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-003/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 28 Nov 2020 00:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-003/</guid><category term="OpenSource">OpenSource</category><category term="PHP">PHP</category><description>&lt;p&gt;Been a quiet week, so wasn&amp;rsquo;t expecting to have much to write on here, however a few things happened worth talking about.&lt;/p&gt;
&lt;h2 id="my-first-pr"&gt;My First PR&lt;a class="anchor ms-1" href="#my-first-pr" aria-label="Permalink: My First PR"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A comment was made to me to do something with the postcodes that are in the system I am developing. Find out what projects have postcodes near each other, and that way work can be grouped together and reduce potential mileage costs of staff that need to visit these projects.&lt;/p&gt;
&lt;p&gt;A quick google search found &lt;a href="https://postcodes.io/" target="_blank" rel="noopener noreferrer"&gt;https://postcodes.io/&lt;/a&gt;
which has an API that returns nearby postcodes. It also has a C# wrapper &lt;a href="https://github.com/markembling/MarkEmbling.PostcodesIO" target="_blank" rel="noopener noreferrer"&gt;https://github.com/markembling/MarkEmbling.PostcodesIO&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;A comparison of what was being returned from the wrapper and what the API said should be returned revealed that the distance between postcodes wasn&amp;rsquo;t being returned.&lt;/p&gt;
&lt;p&gt;As the code was on GitHub I could easily see how easy or difficult it might be to add the missing bit of information. It was easy! So, I forked the repo and made the change. I published the change to a private NuGet repo in my Azure Dev Ops account. That way I could try my revised package to check it did what I wanted.&lt;/p&gt;
&lt;p&gt;I left a message on the GitHub project letting the owner know I had a potential fix for an issue. The project hadn&amp;rsquo;t been updated in over a year, so the owner may not be interested, or the project may have been abandoned.&lt;/p&gt;
&lt;p&gt;I was in luck just 17 hours after I left a message the project owner said to create a Pull Request, which I did and shortly afterwards my code had been merged in and an updated version of the package existed in the public NuGet feed.&lt;/p&gt;
&lt;p&gt;I have been thinking about contributing to open source for a while. However, I had not seen a project I wanted to contribute to, or a problem that I knew how to fix until now that is.&lt;/p&gt;
&lt;h2 id="php"&gt;PHP&lt;a class="anchor ms-1" href="#php" aria-label="Permalink: PHP"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This week I had a call asking me if I knew PHP?&lt;/p&gt;
&lt;p&gt;I did, over ten years ago, before I got my first IT job, I spent time learning PHP and MySQL. I created a blog, and I also created a website for my Dad&amp;rsquo;s camera club. The code I created back then was awful. No shared code, all the code was associated with the page, or sorts of bugs occurred and as time went by it became increasingly hard to update. The site was well liked but I eventually lost interest and moved on to learn other things.&lt;/p&gt;
&lt;p&gt;This call led to me talking with the head of IT, and later a couple of the developers who have since granted me access to the codebase of a project.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t had time to spend a lot of time looking at the code so far, however this is nothing like the PHP I had built before.&lt;/p&gt;
&lt;p&gt;The project makes use of the &lt;a href="https://laravel.com/" target="_blank" rel="noopener noreferrer"&gt;laravel&lt;/a&gt;
framework and the first file I opened had methods and classes, so apart from the syntax you could think you were looking at C#.&lt;/p&gt;
&lt;p&gt;Another thing that interested me was the project used docker containers, it has automated builds as well. Lots of modern programming ideas that I had some ideas about. I am looking forward to learning more about this project and how I might contribute to it.&lt;/p&gt;</description></item><item><title>Weekly Update #002</title><link>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-002/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 21 Nov 2020 00:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-002/</guid><category term="Docker">Docker</category><category term="Azure">Azure</category><description>&lt;p&gt;I know Active Directory is fussy about clocks being in sync however not sure how todays issue happened.&lt;/p&gt;
&lt;p&gt;I run my docker compose file from Visual Studio and I get a weird error.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SecurityTokenNotYetValidException: IDX10222: Lifetime validation failed. The token is not yet valid. ValidFrom: &amp;#39;System.DateTime&amp;#39;, Current time: &amp;#39;System.DateTime&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I deleted my containers, open and close Visual Studio a few times, nothing helps. Eventually I think to find out what the time is on my container. It has yesterday&amp;rsquo;s date. What has happened here? Surely recreating containers would have caused them to have todays date? I reboot and everything is fine again.&lt;/p&gt;
&lt;p&gt;Turns out that it is a know issue, see &lt;a href="https://thorsten-hans.com/docker-on-windows-fix-time-synchronization-issue" target="_blank" rel="noopener noreferrer"&gt;https://thorsten-hans.com/docker-on-windows-fix-time-synchronization-issue&lt;/a&gt;
I am using WSL2 and I have now changed back to using Hyper-V and the issue hasn&amp;rsquo;t come back.&lt;/p&gt;
&lt;p&gt;Earlier in the week I spotted my build step was failing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;task&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;NuGetToolInstaller@0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Swapping to the next version of the step is all I needed to do to fix it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;task&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;NuGetToolInstaller@1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My guess is that support was dropped for this earlier version or there is some other incompatability with .Net 5.&lt;/p&gt;</description></item><item><title>Weekly Update #001</title><link>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-001/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 14 Nov 2020 00:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/weekly-update-001/</guid><category term="DotNet">DotNet</category><description>&lt;p&gt;One of my favourite podcasts is Troy Hunts weekly update. In it he discusses stuff that he has been working on, plus some personal stuff. I am going to attempt to do something similar. It will probably take me a few of these before we get a look and feel that works.&lt;/p&gt;
&lt;h2 id="monday"&gt;Monday&lt;a class="anchor ms-1" href="#monday" aria-label="Permalink: Monday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A week off work, mainly to use it up before year end, plus want to get a few jobs around the house done.&lt;/p&gt;
&lt;p&gt;I did ask the following question on Twitter.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Hey &lt;a href="https://twitter.com/hashtag/azurefamily?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#azurefamily&lt;/a&gt; and &lt;a href="https://twitter.com/hashtag/dotnet?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#dotnet&lt;/a&gt; developers how do I get more involved in mentoring?&lt;/p&gt;&amp;mdash; Simon Foster (@funkysi1701) &lt;a href="https://twitter.com/funkysi1701/status/1325742644014829568?ref_src=twsrc%5Etfw"&gt;November 9, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;As a one person dev team, my biggest weakness is working with others so any ideas of how to change that are great.&lt;/p&gt;
&lt;h2 id="tuesday"&gt;Tuesday&lt;a class="anchor ms-1" href="#tuesday" aria-label="Permalink: Tuesday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dotnet 5 is out! The latest version of dotnet is released by Microsoft and to celebrate there is &lt;a href="https://www.dotnetconf.net/" target="_blank" rel="noopener noreferrer"&gt;dotnetconf&lt;/a&gt;
to listen to. Due to time zones and family commitments, I haven&amp;rsquo;t listened to an awful lot of it but I did see the keynote and loved the 3 Scott&amp;rsquo;s chat.&lt;/p&gt;
&lt;h2 id="wednesday"&gt;Wednesday&lt;a class="anchor ms-1" href="#wednesday" aria-label="Permalink: Wednesday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My youngest son was 3 today, due to Coronavirus we didn&amp;rsquo;t do much but we celebrated as a family, and he even had a zoom call.&lt;/p&gt;
&lt;h2 id="thursday"&gt;Thursday&lt;a class="anchor ms-1" href="#thursday" aria-label="Permalink: Thursday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Blazor has a new feature &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/virtualization?view=aspnetcore-5.0" target="_blank" rel="noopener noreferrer"&gt;Virtualize&lt;/a&gt;
where a list of items can only load the ones on screen. I have been trying to get this to work on my blog, works great running locally but not working in production yet.&lt;/p&gt;
&lt;p&gt;Think I know what might be happening. I use Cloudflare to do my SSL, as Custom SSL certs for the cheaper Azure Web Apps is not supported. Something in Cloudflare is caching or interfering.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://zimmergren.net/solved-asp-net-core-blazor-web-sites-does-not-work-with-cloudflare-html-minification/" target="_blank" rel="noopener noreferrer"&gt;https://zimmergren.net/solved-asp-net-core-blazor-web-sites-does-not-work-with-cloudflare-html-minification/&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Turning off HTML minification fixed my issue!&lt;/p&gt;
&lt;p&gt;One additional thing I added to my Blog is the config page which details some of the config settings. I think this probably came from &lt;a href="https://www.hanselman.com/blog/adding-a-git-commit-hash-and-azure-devops-build-number-and-build-id-to-an-aspnet-website" target="_blank" rel="noopener noreferrer"&gt;https://www.hanselman.com/blog/adding-a-git-commit-hash-and-azure-devops-build-number-and-build-id-to-an-aspnet-website&lt;/a&gt;
but it was a while ago when I first did this on another project.&lt;/p&gt;
&lt;p&gt;At the moment we have .net Version, Commit and Build links.&lt;/p&gt;
&lt;p&gt;The .Net Version is obtained from&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few other bits of info can be obtained from System.Runtime.InteropServices.RuntimeInformation which I have included on the page for fun. There are probably security concerns with exposing all this info publicly so something to bear in mind if you try this.&lt;/p&gt;
&lt;p&gt;Build Info is passed to my code by a build step&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;script&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;(echo $(Build.BuildNumber) &amp;amp;&amp;amp; echo $(Build.BuildId)) &amp;gt; .buildinfo.json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;displayName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Emit build number&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;workingDirectory&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;$(Build.SourcesDirectory)/src/WebBlog&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;failOnStderr&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This simply passed the build id and number which are stored as variabled and saves them in a text file.&lt;/p&gt;
&lt;p&gt;I then have a class that reads them and constructs a link.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; Microsoft.Extensions.Hosting;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.IO;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.Linq;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.Reflection;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;namespace&lt;/span&gt; WebBlog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AppVersionInfo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _buildFileName = &lt;span style="color:#e6db74"&gt;&amp;#34;.buildinfo.json&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;readonly&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _buildFilePath;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _buildNumber = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _buildId = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _gitHash = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; _gitShortHash = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; AppVersionInfo(IHostEnvironment hostEnvironment)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _buildFilePath = Path.Combine(hostEnvironment.ContentRootPath, _buildFileName);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; BuildNumber
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_buildNumber))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (File.Exists(_buildFilePath))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; fileContents = File.ReadLines(_buildFilePath).ToList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (fileContents.Count &amp;gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _buildNumber = fileContents[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (fileContents.Count &amp;gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _buildId = fileContents[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_buildNumber))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _buildNumber = DateTime.UtcNow.ToString(&lt;span style="color:#e6db74"&gt;&amp;#34;yyyyMMdd&amp;#34;&lt;/span&gt;) + &lt;span style="color:#e6db74"&gt;&amp;#34;.0&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_buildId))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _buildId = &lt;span style="color:#e6db74"&gt;&amp;#34;123456&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _buildNumber;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; BuildId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_buildId))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; _ = BuildNumber;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _buildId;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; GitHash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_gitHash))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; version = &lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0+LOCALBUILD&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; appAssembly = &lt;span style="color:#66d9ef"&gt;typeof&lt;/span&gt;(AppVersionInfo).Assembly;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; infoVerAttr = (AssemblyInformationalVersionAttribute)appAssembly
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .GetCustomAttributes(&lt;span style="color:#66d9ef"&gt;typeof&lt;/span&gt;(AssemblyInformationalVersionAttribute)).FirstOrDefault();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (infoVerAttr != &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; infoVerAttr.InformationalVersion.Length &amp;gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version = infoVerAttr.InformationalVersion;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _gitHash = version[(version.IndexOf(&lt;span style="color:#e6db74"&gt;&amp;#39;+&amp;#39;&lt;/span&gt;) + &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)..];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _gitHash;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; ShortGitHash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.IsNullOrEmpty(_gitShortHash))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _gitShortHash = GitHash.Substring(GitHash.Length - &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _gitShortHash;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The BuildId and BuildNumber properties just fetch the details saved into the text file during the build. This can then be passed to build the build link.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;https://dev.azure.com/{OrgName}/{RepoName}/_build/results?buildId=@appInfo.BuildId&amp;amp;view=results&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @appInfo.BuildNumber
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the GitHash properties need to fetch the hash and shorthash of the commit which is a bit more complex. This is achieved using the following line in your build.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;task&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;DotNetCoreCLI@2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;displayName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;Publish&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;publish&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;publishWebProjects&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;arguments&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;--output $(Build.ArtifactStagingDirectory) /p:SourceRevisionId=$(Build.SourceVersion)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;/p:SourceRevisionId=$(Build.SourceVersion) add the revision hash to [assembly: AssemblyInformationalVersion] during the build which can then be extracted using the gitHash property above, before being passed into the commit link.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;https://github.com/{OrgName}/{RepoName}/commit/@appInfo.GitHash&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @appInfo.ShortGitHash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Security Headers</title><link>https://blog-dev.funkysi1701.com/posts/2020/security-headers/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 26 Sep 2020 00:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/security-headers/</guid><category term="DotNet">DotNet</category><category term="Security">Security</category><description>&lt;p&gt;Have you wondered what info you are leaking via your response headers?, do you want some kind of guide about what headers to set or remove altogether?&lt;/p&gt;
&lt;p&gt;Head on over to &lt;a href="https://securityheaders.com/" target="_blank" rel="noopener noreferrer"&gt;https://securityheaders.com/&lt;/a&gt;
This is a site created by security expert &lt;a href="https://scotthelme.co.uk" target="_blank" rel="noopener noreferrer"&gt;Scott Helme&lt;/a&gt;
that rates a URL based on what response headers it can see.&lt;/p&gt;
&lt;p&gt;I am pleased to say &lt;a href="https://www.funkysi1701.com" target="_blank" rel="noopener noreferrer"&gt;https://www.funkysi1701.com&lt;/a&gt;
is now getting an A.
&lt;img class="img-fluid" alt="Alt Text" src="https://blog-dev.funkysi1701.com/images/2020/07rwumi94fz141hwessu.png" loading="lazy"
width="2434" height="496"
/&gt;
&lt;/p&gt;
&lt;p&gt;So how do you add/remove headers in dotnet core?&lt;/p&gt;
&lt;p&gt;In my configure method in Startup.cs I have the following code block.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.Use(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; context =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.Response.OnStarting(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; () =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.Response.Headers.Add(&lt;span style="color:#e6db74"&gt;&amp;#34;Permissions-Policy&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;microphone=()&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.Response.Headers.Remove(&lt;span style="color:#e6db74"&gt;&amp;#34;Server&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.Response.Headers.Remove(&lt;span style="color:#e6db74"&gt;&amp;#34;X-Powered-By&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context.Response.Headers.Remove(&lt;span style="color:#e6db74"&gt;&amp;#34;X-AspNet-Version&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; Task.CompletedTask;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; next(context);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I have only included a few of the headers I am adding as the excellent &lt;a href="https://securityheaders.com/" target="_blank" rel="noopener noreferrer"&gt;https://securityheaders.com/&lt;/a&gt;
can tell you which headers you should add and what options you might want.&lt;/p&gt;</description></item><item><title>Returning to blogging as a developer</title><link>https://blog-dev.funkysi1701.com/posts/2020/back-to-blogging/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Fri, 25 Sep 2020 20:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/back-to-blogging/</guid><category term="Blogging">Blogging</category><category term="Blazor">Blazor</category><category term="DotNet">DotNet</category><description>&lt;p&gt;My last blog post was over six months ago.&lt;/p&gt;
&lt;p&gt;Covid 19 has hit the world, and I will be honest I have found it a challenging time.&lt;/p&gt;
&lt;p&gt;My Blog had gotten into a bit of a mess. It had become fragmented with different versions of the same thing; I will attempt to explain what has become of my blog.&lt;/p&gt;
&lt;p&gt;The original WordPress site can currently be found at &lt;a href="https://www.pwnedpass.com/" target="_blank" rel="noopener noreferrer"&gt;https://www.pwnedpass.com/&lt;/a&gt;
I would prefer it to be on a sub-domain of funkysi1701.com but for some reason I haven&amp;rsquo;t been able to get that to work, not sure if it is a limitation of my hosting package. I like WordPress, it is very flexible, easy to get blog posts out there. But I want to write content about development and having a site I can tinker with is important to me.&lt;/p&gt;
&lt;p&gt;Most of my WordPress blogs have been imported into dev.to and a few extra have been written on this platform. I like dev.to it is a wonderful place to share content and it has one or two extra features I like.&lt;/p&gt;
&lt;p&gt;dev.to has an integration with Stackbit/Netlify and this became &lt;a href="https://www.funkysi1701.com" target="_blank" rel="noopener noreferrer"&gt;https://www.funkysi1701.com&lt;/a&gt;
. I like having a personal site, but having the same content as dev.to. To add content to this site all I need to do is write it on dev.to and some magic will go on behind the scenes and new content will be published.&lt;/p&gt;
&lt;p&gt;However, as a developer I don&amp;rsquo;t like magic, I want to understand what is going on a fiddle with all the settings and make it do what I want.&lt;/p&gt;
&lt;p&gt;dev.to has an API, I can build a site in .Net Core and make API calls to fetch the content I want. I understand APIs, I understand .Net and can customise my site exactly how I want it, plus play about with a .net website. This is what &lt;a href="https://www.funkysi1701.com" target="_blank" rel="noopener noreferrer"&gt;https://www.funkysi1701.com&lt;/a&gt;
is now.&lt;/p&gt;
&lt;p&gt;So what have I built so far. I have a Server Side Blazor site running .Net 5. Why Server Side and not Client Side I hear you ask? Well only because I have more experience with Server Side and know how to quickly create a website with that technology, I may change it as time goes by, but we will see.&lt;/p&gt;
&lt;p&gt;I have two pages a list of my blog posts and a page that displays the content. Both of these use the dev.to API. I lied, there is a third page I hacked together to do some page redirection from the WordPress URLs. This is something I will change as time goes on.&lt;/p&gt;
&lt;p&gt;There are lots of improvements I want to do, there are probably also lots of broken images or links as well. Hopefully, this will result in a good platform to blog about as well as on.&lt;/p&gt;</description></item><item><title>Testing expiring SSL certificates</title><link>https://blog-dev.funkysi1701.com/posts/2020/testing-for-expiring-ssl-certificates/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Tue, 03 Mar 2020 20:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2020/testing-for-expiring-ssl-certificates/</guid><category term="Security">Security</category><category term="SSL">SSL</category><category term="Testing">Testing</category><description>&lt;p&gt;Let&amp;rsquo;s Encrypt is amazing, you can easily add SSL certificates to any website and automate the renewal process. I have talked &lt;a href="https://www.funkysi1701.com/posts/2018/lets-encrypt-is-awesome/" target="_blank" rel="noopener noreferrer"&gt;before&lt;/a&gt;
about how impressive it is.&lt;/p&gt;
&lt;p&gt;Once you start adding SSL certificates to your production sites however you may want to check when they expire so you don&amp;rsquo;t get caught out. You can always open your site in your favourite browser and view the certificate information and expiry date.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="SSL Cert" src="https://blog-dev.funkysi1701.com/images/2020/jb78re4fmm1ofx81f3mu.jpg" loading="lazy"
width="671" height="608"
/&gt;
However there is a way to automate this check.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[Fact]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; IsSSLExpiring()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; handler = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; HttpClientHandler
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ServerCertificateCustomValidationCallback = CustomCallback
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; client = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; HttpClient(handler);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HttpResponseMessage response = client.GetAsync(&lt;span style="color:#e6db74"&gt;&amp;#34;https://www.example.com&amp;#34;&lt;/span&gt;).GetAwaiter().GetResult();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Assert.True(response.IsSuccessStatusCode);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; CustomCallback(HttpRequestMessage arg1, X509Certificate2 arg2, X509Chain arg3, SslPolicyErrors arg4)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; now = DateTime.UtcNow;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; expire = arg2.NotAfter;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; diff = (expire - now).TotalDays;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Assert.InRange(diff, &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; arg4 == SslPolicyErrors.None;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code gets the SSL expiry date from &lt;a href="https://www.example.com" target="_blank" rel="noopener noreferrer"&gt;https://www.example.com&lt;/a&gt;
and will fail the xunit test if the expiry date is less than 30 days in the future. I then schedule my tests to run regularly on all my environments with a Let&amp;rsquo;s Encrypt Certificate and this gives me advanced warning if a SSL certificate is about to expire.&lt;/p&gt;
&lt;p&gt;The Assert.InRange(diff, 30, 1000) line will fail the test if the expiry date is less than 30 days or greater than 1000, but as the default expiry for Let&amp;rsquo;s Encrypt certificates is three months it will never be greater than 1000 days even with a freshly installed certificate. These values can be tweaked to suit your use case, however 30 days is enough time for me to investigate what is happening.&lt;/p&gt;
&lt;p&gt;To execute my tests I use a scheduled build in Azure DevOps, but anything that regularly can run your tests will do the job.&lt;/p&gt;
&lt;p&gt;The code above is just a simple example to get your started for my purposes I have put all my URLs into config files and just pass these into my tests, so I don&amp;rsquo;t need a custom test for every different URL.&lt;/p&gt;</description></item></channel></rss>