<?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"><channel><title><![CDATA[Venky Writes]]></title><description><![CDATA[Web architect cum developer working in the .NET ecosystem with Azure and having completed many successful projects and products mainly Syncfusion Data Platform - Bold BI and Bold Reports.]]></description><link>https://blogs.aspnet.in</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1707583401584/GYy8k65le.png</url><title>Venky Writes</title><link>https://blogs.aspnet.in</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 06:25:40 GMT</lastBuildDate><atom:link href="https://blogs.aspnet.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Metrics with IMeterFactory and Azure Application Insights]]></title><description><![CDATA[Level Up Your .NET 8 ASP.NET Core App: Metrics with IMeterFactory and Azure Application Insights
In the world of modern application development, observability is king. You need to know how your application is performing, and metrics are a crucial par...]]></description><link>https://blogs.aspnet.in/metrics-with-imeterfactory-and-azure-application-insights</link><guid isPermaLink="true">https://blogs.aspnet.in/metrics-with-imeterfactory-and-azure-application-insights</guid><category><![CDATA[monitoring]]></category><category><![CDATA[OpenTelemetry]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Wed, 05 Mar 2025 14:49:53 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-level-up-your-net-8-aspnet-core-app-metrics-with-imeterfactory-and-azure-application-insights">Level Up Your .NET 8 ASP.NET Core App: Metrics with IMeterFactory and Azure Application Insights</h1>
<p>In the world of modern application development, observability is king. You need to know how your application is performing, and metrics are a crucial part of that. In .NET 8, <code>IMeterFactory</code> provides a powerful and standardized way to instrument your code. Coupled with Azure Application Insights, you can gain deep insights into your application's behavior. Let's dive into how to set this up in your ASP.NET Core application.</p>
<h2 id="heading-1-project-setup-and-dependencies">1. Project Setup and Dependencies</h2>
<p>First, create a new ASP.NET Core Web API project (or use an existing one). Then, add the necessary NuGet packages:</p>
<pre><code class="lang-markdown">dotnet add package Microsoft.Extensions.Diagnostics.Abstractions
dotnet add package Microsoft.Extensions.Diagnostics.Metrics
dotnet add package Azure.Monitor.OpenTelemetry.AspNetCore
</code></pre>
<p><code>Microsoft.Extensions.Diagnostics.Abstractions</code>: Provides core abstractions for diagnostics. <code>Microsoft.Extensions.Diagnostics.Metrics</code>: Contains the IMeterFactory and related types. <code>Azure.Monitor.OpenTelemetry.AspNetCore</code>: Enables OpenTelemetry integration with Azure Application Insights.</p>
<h2 id="heading-2-configuring-imeterfactory-and-application-insights">2. Configuring IMeterFactory and Application Insights</h2>
<p>Modify your <code>Program.cs</code> file to configure <code>IMeterFactory</code> and enable Application Insights:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Microsoft.Extensions.Diagnostics.Metrics;

<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

<span class="hljs-comment">// Add Application Insights</span>
builder.Services.AddApplicationInsightsTelemetry();

<span class="hljs-comment">// Add MeterFactory</span>
builder.Services.AddMetrics();

<span class="hljs-comment">// ... other services ...</span>

<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// ... middleware ...</span>

app.Run();
</code></pre>
<p>This snippet does the following:</p>
<p><a target="_blank" href="http://builder.Services"><code>builder.Services</code></a><code>.AddApplicationInsightsTelemetry();</code>: Configures Application Insights with OpenTelemetry, automatically collecting telemetry data.</p>
<p><a target="_blank" href="http://builder.Services"><code>builder.Services</code></a><code>.AddMetrics();</code>: Registers the IMeterFactory with the dependency injection container.</p>
<h2 id="heading-3-instrumenting-your-code">3. Instrumenting Your Code</h2>
<p>Now, let's add some instrumentation to your code. For example, we'll track the number of HTTP requests:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;
<span class="hljs-keyword">using</span> System.Diagnostics.Metrics;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">MyWebApi.Controllers</span>;

[<span class="hljs-meta">ApiController</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"[controller]"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">WeatherForecastController</span> : <span class="hljs-title">ControllerBase</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span>[] Summaries = <span class="hljs-keyword">new</span>[]
    {
        <span class="hljs-string">"Freezing"</span>, <span class="hljs-string">"Bracing"</span>, <span class="hljs-string">"Chilly"</span>, <span class="hljs-string">"Cool"</span>, <span class="hljs-string">"Mild"</span>, <span class="hljs-string">"Warm"</span>, <span class="hljs-string">"Balmy"</span>, <span class="hljs-string">"Hot"</span>, <span class="hljs-string">"Sweltering"</span>, <span class="hljs-string">"Scorching"</span>
    };

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;WeatherForecastController&gt; _logger;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Counter&lt;<span class="hljs-keyword">int</span>&gt; _requestCounter;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherForecastController</span>(<span class="hljs-params">ILogger&lt;WeatherForecastController&gt; logger, IMeterFactory meterFactory</span>)</span>
    {
        _logger = logger;
        <span class="hljs-keyword">var</span> meter = meterFactory.Create(<span class="hljs-string">"MyWebApi.Requests"</span>);
        _requestCounter = meter.CreateCounter&lt;<span class="hljs-keyword">int</span>&gt;(<span class="hljs-string">"http.requests.count"</span>, <span class="hljs-string">"requests"</span>, <span class="hljs-string">"Counts incoming HTTP requests."</span>);
    }

    [<span class="hljs-meta">HttpGet(Name = <span class="hljs-meta-string">"GetWeatherForecast"</span>)</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> IEnumerable&lt;WeatherForecast&gt; <span class="hljs-title">Get</span>(<span class="hljs-params"></span>)</span>
    {
        _requestCounter.Add(<span class="hljs-number">1</span>);
        <span class="hljs-keyword">return</span> Enumerable.Range(<span class="hljs-number">1</span>, <span class="hljs-number">5</span>).Select(index =&gt; <span class="hljs-keyword">new</span> WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(<span class="hljs-number">-20</span>, <span class="hljs-number">55</span>),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
}
</code></pre>
<ul>
<li><p>We inject IMeterFactory into the controller's constructor.</p>
</li>
<li><p>We create a Meter with a meaningful name ("MyWebApi.Requests").</p>
</li>
<li><p>We create a Counter to track the request count.</p>
</li>
<li><p>We increment the counter each time the endpoint is called.</p>
</li>
<li><p>Remember to use descriptive names and units.</p>
</li>
</ul>
<h2 id="heading-4-visualizing-metrics-in-azure-application-insights">4. Visualizing Metrics in Azure Application Insights</h2>
<p>After deploying your application to Azure, navigate to your Application Insights resource in the Azure portal.</p>
<ul>
<li><p>Metrics Explorer:</p>
<ul>
<li><p>Go to "Metrics" in the left-hand menu.</p>
</li>
<li><p>In the "Metric Namespace" dropdown, you'll find your custom meter namespace ("MyWebApi.Requests" in our example).</p>
</li>
<li><p>Select your counter ("http.requests.count").</p>
</li>
<li><p>You can then visualize the request count over time.</p>
</li>
</ul>
</li>
</ul>
<p>*Logs (Analytics):</p>
<ul>
<li><p>Go to "Logs" in the left-hand menu.</p>
</li>
<li><p>Use Kusto Query Language (KQL) to query your metrics data. For example:</p>
</li>
</ul>
<pre><code class="lang-markdown">customMetrics
| where customMetricName == "http.requests.count"
| summarize sum(valueCount) by bin(timestamp, 1m)
| render timechart
</code></pre>
<p>This query will display a time chart of the total request count aggregated by minute.</p>
<h2 id="heading-5-adding-tags-for-context">5. Adding Tags for Context</h2>
<p>To add more context to your metrics, use tags:</p>
<pre><code class="lang-csharp">_requestCounter.Add(<span class="hljs-number">1</span>, <span class="hljs-keyword">new</span> KeyValuePair&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">object</span>&gt;(<span class="hljs-string">"http.method"</span>, <span class="hljs-string">"GET"</span>));
</code></pre>
<p>Then, you can filter and aggregate metrics by these tags in Application Insights.</p>
<h2 id="heading-best-practices">Best Practices</h2>
<ul>
<li><p>Use meaningful meter and instrument names.</p>
</li>
<li><p>Choose the appropriate instrument type (Counter, Gauge, Histogram).</p>
</li>
<li><p>Leverage tags for filtering and aggregation.</p>
</li>
<li><p>Use UCUM for units.</p>
</li>
<li><p>Monitor Application Insights regularly.</p>
</li>
</ul>
<p>By following these steps, you can effectively use IMeterFactory and Azure Application Insights to gain valuable insights into your .NET 8 ASP.NET Core application's performance. Happy monitoring!</p>
]]></content:encoded></item><item><title><![CDATA[Clean as you code approach — SonarQube Analysis]]></title><description><![CDATA[What is “clean as you code” approach?
Clean as you code is an approach to code quality that eliminates many of the challenges that come with traditional approaches. As a developer, you focus on maintaining high standards and taking responsibility spe...]]></description><link>https://blogs.aspnet.in/clean-as-you-code-approach-sonarqube-analysis</link><guid isPermaLink="true">https://blogs.aspnet.in/clean-as-you-code-approach-sonarqube-analysis</guid><category><![CDATA[static code analysis]]></category><category><![CDATA[sonarqube]]></category><category><![CDATA[code review]]></category><category><![CDATA[Code Quality]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Sun, 25 Feb 2024 14:58:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708872740340/10138c69-2502-4749-876f-980e2a8c6c50.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-what-is-clean-as-you-code-approach"><strong>What is “clean as you code” approach?</strong></h1>
<p>Clean as you code is an approach to code quality that eliminates many of the challenges that come with traditional approaches. As a developer, you focus on maintaining high standards and taking responsibility specifically in the new code you’re working on. SonarQube gives you the tools to set high standards and take pride in knowing that your code meets those standards.</p>
<p>This approach lets you focus on whether the new code you write is clean and safe and you take responsibility only for the code you write. This approach also lets the reviewer see only the analysis of the new code and not the code that was written before introducing SonarQube analysis.</p>
<h1 id="heading-prerequisite"><strong>Prerequisite</strong></h1>
<p>For you to follow this approach, the project in the SonarQube server should know the analysis report of the branch or a particular code version or a specific analysis or code version with a floating period which will be used for comparison.</p>
<p>Setting your new code definition is explained here — <a target="_blank" href="https://docs.sonarqube.org/latest/project-administration/defining-new-code">https://docs.sonarqube.org/latest/project-administration/defining-new-code</a></p>
<p>So, the prerequisite is an analysis report to which the changed code in PR’s analysis report can be compared.</p>
<h1 id="heading-branchmainmaster-analysis-for-a-new-project-in-sonarqube"><strong>Branch(main/master) analysis for a new project in SonarQube</strong></h1>
<p>When a new project is added in SonarQube for your source code, the analysis would be empty, and you must feed the base analysis report (Overall Code) which you decide to go with.</p>
<p>This is a step that should be done before any PR pipeline with SonarQube analysis is created.</p>
<p>For this documentation purpose, the default branch is analyzed and fed into the SonarQube server.</p>
<p>This base analysis can be done in two ways,</p>
<ol>
<li><p>From the DevOps pipeline</p>
</li>
<li><p>Using the dotnet-sonarscanner tool — <a target="_blank" href="https://www.nuget.org/packages/dotnet-sonarscanner">https://www.nuget.org/packages/dotnet-sonarscanner</a></p>
</li>
</ol>
<p>This is a one-time step based on your new code definition.</p>
<p>The usual setup for SonarQube analysis would be in the PR build pipeline which would do the following,</p>
<ol>
<li><p>Pull the source (task) branch</p>
</li>
<li><p>Prepare the SonarQube analysis</p>
</li>
<li><p>Build the source code</p>
</li>
<li><p>Run the SonarQube Analysis</p>
</li>
<li><p>Publish the Analysis to the SonarQube server in the PR’s name</p>
</li>
</ol>
<p>The same pipeline would be triggered after the PR is completed to run the analysis against the destination (main/master) branch. This will update the existing base report (Overall Code) analysis with the latest analysis.</p>
<p>If this is the flow that the project needs, then the recommended approach is to use the dotnet-sonarscanner tool as a one-time step after the project is created.</p>
<p>If your project needs to set the new code definition dynamically or want scan the main branch without setting up the source code in your machine, then running the analysis in the pipeline would be useful.</p>
<p>However, for the first-time setup, if you take the DevOps pipeline approach to feed the base analysis report, then you must override the Quality gate and complete the PR to trigger the analysis on the main branch.</p>
<h1 id="heading-dotnet-tool-dotnet-sonarscanner"><strong>Dotnet tool dotnet-sonarscanner</strong></h1>
<p>Using this tool, you can scan any branch in your source code and publish it to the SonarQube server. The tool will detect the git branch name for you and will be pushed along with the analysis.</p>
<p>Once you add the project in SonarQube for your source code repo, you will be shown a page like the one below. Choose the manual approach here,</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*X1jQIqGG48UwLS_VEuUg4w.png" alt /></p>
<p>Follow the below steps,</p>
<h2 id="heading-step-1-setup-your-token-for-analysis"><strong>Step 1: Setup your token for analysis</strong></h2>
<p>Give the token a name and generate it,</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*ZKUUD4_WJwS_gfjBI2B5qg.png" alt /></p>
<p>You can always use an existing token if you have one. This is the token you will use in the dotnet tool for authenticating you and publishing the analysis report on your machine.</p>
<h2 id="heading-step-2-run-the-analysis"><strong>Step 2: Run the analysis</strong></h2>
<p>Prerequisite for this step,</p>
<p><strong>Prerequisite 1</strong></p>
<p>Install the dotnet tool dotnet-sonarscanner using the below command,</p>
<pre><code class="lang-bash">dotnet tool install --global dotnet-sonarscanner --version 6.2.0
</code></pre>
<p>6.2.0 is the latest version when writing this documentation.</p>
<p><strong>Prerequisite 2</strong></p>
<p>Checkout the source code of the project you are configuring the analysis and make sure you have the branch you want to scan and set as the base report (Overall Code) analysis.</p>
<p>Once the prerequisites are completed, you can execute the scanner on the root of your source code.</p>
<p>Running a SonarQube analysis is straightforward. You just need to execute the following commands at the root of your solution.</p>
<p><strong>SonarScanner begin command</strong></p>
<p>This command will ensure the tracking of code analysis when you build the project.</p>
<pre><code class="lang-bash">dotnet sonarscanner begin /k:<span class="hljs-string">"Project.Name"</span> /d:sonar.host.url=<span class="hljs-string">"https://sonarqubeapp.azurewebsites.net"</span>  /d:sonar.login=<span class="hljs-string">"login.id.here"</span>
</code></pre>
<p><strong>Dotnet build command</strong></p>
<p>Now you can build the project with the dotnet build command.</p>
<pre><code class="lang-bash">dotnet build
</code></pre>
<p><strong>SonarScanner end command</strong></p>
<p>This command will collect the code analysis and publish it to the SonarQube server.</p>
<pre><code class="lang-bash">dotnet sonarscanner end /d:sonar.login=<span class="hljs-string">"login.id.here"</span>
</code></pre>
<p>All these commands and instructions are shown in the setup page as like below,</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*bIl_Ccv6bj6Cw1Zvy9QOCQ.png" alt /></p>
<h1 id="heading-using-the-devops-pipeline-for-branch-analysis"><strong>Using the DevOps pipeline for branch analysis</strong></h1>
<p>This is the usual step you do in a build pipeline using the SonarQube tasks in the DevOps. Check the below blog on how to do this,</p>
<p><a target="_blank" href="https://blogs.aspnet.in/sonarqube-deployment-integration-and-configuration">https://blogs.aspnet.in/sonarqube-deployment-integration-and-configuration</a></p>
<h2 id="heading-overall-code"><strong>Overall Code</strong></h2>
<p>Once the branch analysis and publishing are done from the above steps either using the dotnet tool or the DevOps pipeline, you should see the measures in the Overall Code tab as shown below,</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*KEsnQU2-LIr5CT3IlmX8qg.png" alt /></p>
<p>This will be the default base report analysis which will be used for comparison(until the next scan happens on the same branch) when a PR analysis report is published.</p>
<p>The new code tab will be empty at this point.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*0wu8LdZ9zoPaRZp3s7YE6Q.png" alt /></p>
<h2 id="heading-pr-pipeline-analysis-report-comparison"><strong>PR pipeline analysis report comparison</strong></h2>
<p>Now, let's introduce a code smell issue in a task branch and see the analysis report.<br />In the New Code tab, you will now see the issue that was identified in the PR as shown below. Note that the 33 code smells in the Overall Code are not reported here.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*7dGqgjYvAHpOMzSRPXGl8w.png" alt /></p>
<p>Only the new issues are reported which lets you focus on them and ignore the existing code. This is how we follow the “Clean as you code” approach.</p>
]]></content:encoded></item><item><title><![CDATA[SonarQube - Deployment, Integration and Configuration]]></title><description><![CDATA[Deployment
SonarQube is a web service that can run in our Azure App Service. It uses an SQL database to store code analysis reports.
We can use the SonarQube docker available in Docker Hub to deploy the app into Azure App Service.
Here is the docker-...]]></description><link>https://blogs.aspnet.in/sonarqube-deployment-integration-and-configuration</link><guid isPermaLink="true">https://blogs.aspnet.in/sonarqube-deployment-integration-and-configuration</guid><category><![CDATA[static code analysis]]></category><category><![CDATA[sonarqube]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Thu, 22 Feb 2024 19:39:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708630875906/75800657-59dd-4625-8a47-4b25e2542ee4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-deployment">Deployment</h3>
<p>SonarQube is a web service that can run in our Azure App Service. It uses an SQL database to store code analysis reports.</p>
<p>We can use the SonarQube docker available in Docker Hub to deploy the app into Azure App Service.</p>
<p>Here is the docker-compose.yml,</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">sonarqube:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">sonarqube:lts-enterprise</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">SONARQUBE_JDBC_URL:</span> <span class="hljs-string">jdbc:sqlserver://testsql.database.windows.net:1433;database=sonarqube-enterprise-edition;user=sonarqube@sql-server-sonarqube;password=Admin@123;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;</span>
      <span class="hljs-attr">SONARQUBE_JDBC_USERNAME:</span> <span class="hljs-string">sonarqube</span>
      <span class="hljs-attr">SONARQUBE_JDBC_PASSWORD:</span> <span class="hljs-string">Admin@123</span>
      <span class="hljs-attr">WEBSITES_PORT:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">sonarqube_data:/opt/sonarqube/data</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">sonarqube_extensions:/opt/sonarqube/extensions</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">sonarqube_logs:/opt/sonarqube/logs</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">sonarqube_temp:/opt/sonarqube/temp</span>   
    <span class="hljs-attr">ulimits:</span>
      <span class="hljs-attr">nproc:</span> <span class="hljs-number">131072</span>
      <span class="hljs-attr">nofile:</span>
        <span class="hljs-attr">soft:</span> <span class="hljs-number">8192</span>
        <span class="hljs-attr">hard:</span> <span class="hljs-number">131072</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:9000"</span>
<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">sonarqube_data:</span>
  <span class="hljs-attr">sonarqube_extensions:</span>
  <span class="hljs-attr">sonarqube_logs:</span>
  <span class="hljs-attr">sonarqube_temp:</span>
</code></pre>
<p>The credential for the database is added in the docker-compose.yml file. This is needed when the app is started.</p>
<p>We can also configure the same from the Configuration.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*YMxIQR095lllJtPr3Wgd1A.png" alt /></p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>You can check SonarQube’s requirements here — <a target="_blank" href="https://docs.sonarqube.org/latest/requirements/requirements/">https://docs.sonarqube.org/latest/requirements/requirements/</a></p>
<h4 id="heading-app-service">App Service</h4>
<p>For the app to run in Azure App Service, we need a <strong>P2V2</strong> plan. This is because the app internally has an Elastic Search service, and it needs more memory. Otherwise, you will see the app running and getting stopped after some time.</p>
<p>You can always use the Log Stream in the App Service to watch the logs from the app.</p>
<h4 id="heading-database">Database</h4>
<p>MS SQL Database must have case-sensitive and accent-sensitive collation.</p>
<p>SQL_Latin1_General_CP1_CS_AS</p>
<p>When you start the application for the first time, it will ask you to reset the default password. Default credentials will be — username: admin, password: admin.</p>
<h3 id="heading-integration">Integration</h3>
<p>You have the below integration steps to do the SonarQube analysis,</p>
<ol>
<li><p>Adding your DevOps repo in SonarQube</p>
</li>
<li><p>Updating your DevOps pipelines with SonarQube Analysis and Reporting tasks</p>
</li>
</ol>
<p>Here is the SonarQube documentation for the same,</p>
<p><a target="_blank" href="https://docs.sonarqube.org/latest/analysis/azuredevops-integration/">https://docs.sonarqube.org/latest/analysis/azuredevops-integration</a></p>
<p>Below you will see the simplified steps of the same documentation,</p>
<p><strong>Prerequisites</strong></p>
<p>1. Personal Access Token must be generated with Read and Write Access for the Code module. This is needed in the SonarQube project addition module.</p>
<p>2. Install the SonarQube extension in Azure DevOps — <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube">https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube</a></p>
<p>3. Access to your DevOps repo settings for adding the SonarQube service connection to it.</p>
<p><strong>Adding your DevOps repo in SonarQube</strong></p>
<p><strong>Repo addition</strong></p>
<p>Start by clicking on the “Add Project” button and selecting DevOps. This will be a self-explanatory form.</p>
<ol>
<li><p>Feed in the PAT you obtained for your account in DevOps with Read and Write Access for the Code module.</p>
</li>
<li><p>Using the PAT, SonarQube will list the Projects in DevOps. Find your DevOps project and choose the repo.</p>
</li>
</ol>
<p>This will create the project in SonarQube.</p>
<p><strong>Updating your DevOps pipelines with SonarQube Analysis and Reporting tasks</strong></p>
<p>Once you add the project, you can configure the analysis in your DevOps pipeline. Add the SonarQube server URL as a service connection in your project settings.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*tRMVs0kE3yxV6SJhGd0FyQ.png" alt /></p>
<p><strong>Configure Analysis</strong></p>
<p>In your DevOps pipeline, do the following,</p>
<ol>
<li><p>Add the SonarQube Preparation task before your Build task</p>
</li>
<li><p>Add the SonarQube Analysis task after your Build task</p>
</li>
<li><p>Add the SonarQube Publish task after the analysis task</p>
</li>
</ol>
<p><img src="https://cdn-images-1.medium.com/max/800/1*htyVSQDx51G9vdq-UD2rmw.png" alt /></p>
<p>SonarQube preparation task must be fed with the service connection we created in the previous step.</p>
<p>Now, if you run your pipeline, you should see the analysis report in the SonarQube project below,</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*bdHiNpAkX5sgIS_JSHd1pw.png" alt /></p>
<h4 id="heading-configuration-of-sonarqube-rules">Configuration of SonarQube rules</h4>
<p>SonarQube has an inbuilt ruleset that is categorized in different facets, and it is synchronized with new SonarQube release versions too.</p>
<p>You will find detailed documentation here — <a target="_blank" href="https://docs.sonarqube.org/latest/user-guide/rules/">https://docs.sonarqube.org/latest/user-guide/rules</a></p>
<p>Here we will see the simplified version of the same,</p>
<p>SonarQube has around 400+ rules spanning several types =&gt; bugs, vulnerability, code smell, and security hotspots for C#.</p>
<p>Hierarchically, rules come below Quality Profiles. Rules can be grouped under a Quality profile and these profiles can be assigned to the projects as needed.</p>
<p>SonarQube comes with a default quality profile for each language and cannot be edited. But it can be extended with new rules.</p>
<p>New Quality profiles can be created based on the existing ones. Under the new quality profiles, rules can be activated/deactivated based on the needs.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*FImy2pxJu9SK4iVmlsq1vw.png" alt /></p>
<p><strong>Activating/Deactivating a Rule in a Quality Profile</strong></p>
<p>Steps,</p>
<ol>
<li><p>Open the Quality Profile.</p>
</li>
<li><p>On the left side, you will the rule types with their active and inactive counts.</p>
</li>
<li><p>You can click on any of the counts to filter and see the rules that are active/inactive.</p>
</li>
<li><p>Once you are on the Rules page, make sure you are editing the correct Quality profile by seeing that on the left side.</p>
</li>
<li><p>You can also make bulk changes based on your needs.</p>
</li>
</ol>
<p><img src="https://cdn-images-1.medium.com/max/800/1*orXtB6vm7jj-BoAY58bTIQ.png" alt /></p>
<p>Once the activation or deactivation of the rules is done, it will immediately reflect in your existing reports as well. So, everything is dynamic.</p>
<p>Even for your older analysis reports, you can activate new rules.</p>
<p><strong>Quality Gates</strong></p>
<p>This is an optional module that can be set up in your Azure DevOps branch policy to ensure that your repo is complying with all the rules you set up. This gate can be used to prevent the PR (Pull Request) from being merged.</p>
<p>As a process, you do not have to set this up initially and you will have to mandate this once you fix all the rules.</p>
<p><a target="_blank" href="https://docs.sonarsource.com/sonarqube/latest/user-guide/quality-gates/">https://docs.sonarsource.com/sonarqube/latest/user-guide/quality-gates/</a></p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*uPmv-4lwUKCCBjeedghVtA.png" alt /></p>
<p><strong>SonarQube Report summary in DevOps</strong></p>
<p>You will always have a report summary in your pipeline, under extensions and you can open the SonarQube site report from here.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*62x9c3bMhK5fcHndKHZwPw.png" alt /></p>
<p><strong>SonarLint Extension in VS</strong></p>
<p>We can install the SonarLint extension in VS and get the analysis while doing the development itself.</p>
<p>Here is the link for SonarLint Extension,</p>
<p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=SonarSource.SonarLintforVisualStudio2022">https://marketplace.visualstudio.com/items?itemName=SonarSource.SonarLintforVisualStudio2022</a></p>
<p>As SonarQube supports different quality profiles for different projects, the project that you run in VS also needs the same Quality profile so that we can coordinate with the rule sets configured in the project. To do this, we can connect the SonarLint in VS to our SonarQube server.</p>
<h3 id="heading-connect-sonarlint-to-sonarqube-server">Connect SonarLint to SonarQube Server</h3>
<p>Here is the documentation on how to do this,</p>
<p><a target="_blank" href="https://github.com/SonarSource/sonarlint-visualstudio/wiki/Connected-Mode">https://github.com/SonarSource/sonarlint-visualstudio/wiki/Connected-Mode</a></p>
<p>Here we will see the simplified version of the same,</p>
<ol>
<li><p>Install SonarLint extension.</p>
</li>
<li><p>Go to Team Explorer in VS and select SonarQube.</p>
</li>
</ol>
<p><img src="https://cdn-images-1.medium.com/max/800/1*v4ofhCLCU3SQ40OfeasYYw.png" alt /></p>
<p>3. Feed the credentials.</p>
<p>4. Select the appropriate project and that is all.</p>
]]></content:encoded></item><item><title><![CDATA[C# Extension Methods that come in handy for an ASP.Net Project]]></title><description><![CDATA[In the realm of C# and ASP.NET development, extension methods serve as invaluable tools, empowering developers to enhance the expressiveness and efficiency of their code. In this blog post, we will delve into some of the basic extension methods that ...]]></description><link>https://blogs.aspnet.in/c-extension-methods-that-come-in-handy-for-an-aspnet-project</link><guid isPermaLink="true">https://blogs.aspnet.in/c-extension-methods-that-come-in-handy-for-an-aspnet-project</guid><category><![CDATA[C#]]></category><category><![CDATA[extensions]]></category><category><![CDATA[asp.net core]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Sun, 04 Feb 2024 08:23:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707034887125/f4118caf-7d2f-4b86-874c-c17374e30d32.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the realm of C# and <a target="_blank" href="http://ASP.NET">ASP.NET</a> development, extension methods serve as invaluable tools, empowering developers to enhance the expressiveness and efficiency of their code. In this blog post, we will delve into some of the basic extension methods that any <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core project needs and can elevate your web development projects.</p>
<h1 id="heading-httpcontext-extensions">HTTPContext Extensions</h1>
<h2 id="heading-get-base-path">Get Base Path</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetBasePath</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpContext httpContext</span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{httpContext.Request.Scheme}</span>://<span class="hljs-subst">{httpContext.Request.Host}</span>"</span>;
}
</code></pre>
<h2 id="heading-check-if-the-request-header-has-a-particular-key">Check if the Request Header has a particular key</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">HasRequestHeader</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpContext httpContext, <span class="hljs-keyword">string</span> headerName</span>)</span>
{
    <span class="hljs-keyword">return</span> httpContext.Request.Headers.ContainsKey(headerName);
}
</code></pre>
<h2 id="heading-check-if-the-response-header-has-a-particular-key">Check if the Response Header has a particular key</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">HasResponseHeader</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpContext httpContext, <span class="hljs-keyword">string</span> headerName</span>)</span>
{
    <span class="hljs-keyword">return</span> httpContext.Response.Headers.ContainsKey(headerName);
}
</code></pre>
<h2 id="heading-check-if-the-request-header-key-has-the-required-value">Check if the Request Header key has the required value</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">HasRequestHeaderValue</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpContext httpContext, <span class="hljs-keyword">string</span> headerName, <span class="hljs-keyword">string</span> headerValue</span>)</span>
{
    <span class="hljs-keyword">return</span> httpContext.Request.Headers.TryGetValue(headerName, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> header)
        &amp;&amp; header.Any(a =&gt; a.Equals(headerValue, StringComparison.OrdinalIgnoreCase));
}
</code></pre>
<h2 id="heading-check-if-the-response-header-key-has-the-required-value">Check if the Response Header key has the required value</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">HasResponseHeaderValue</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpContext httpContext, <span class="hljs-keyword">string</span> headerName, <span class="hljs-keyword">string</span> headerValue</span>)</span>
{
    <span class="hljs-keyword">return</span> httpContext.Response.Headers.TryGetValue(headerName, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> header)
        &amp;&amp; header.Any(a =&gt; a.Equals(headerValue, StringComparison.OrdinalIgnoreCase));
}
</code></pre>
<h1 id="heading-ienumerable-extensions">IEnumerable Extensions</h1>
<h2 id="heading-convert-any-ienumerable-to-csv-string">Convert any IEnumerable to CSV String</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ConvertToCSVString</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> IEnumerable&lt;T&gt; items</span>)</span>
{
    <span class="hljs-keyword">if</span> (items.Any())
    {
        <span class="hljs-keyword">var</span> lines = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;();
        <span class="hljs-keyword">var</span> header = <span class="hljs-keyword">string</span>.Empty;

        <span class="hljs-keyword">if</span> (items.FirstOrDefault().GetType() == <span class="hljs-keyword">typeof</span>(JObject))
        {
            <span class="hljs-keyword">var</span> jObject = (JObject)Convert.ChangeType(items.FirstOrDefault(), <span class="hljs-keyword">typeof</span>(JObject));

            header = <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">","</span>, jObject.Properties().Select(x =&gt; x.Name));

            lines.Add(header);

            <span class="hljs-comment"><span class="hljs-doctag">///</span>/DO NOT include the starting and ending quotes inside the $ string interpolation.</span>
            <span class="hljs-keyword">var</span> valueLines = items.Select(row =&gt;
                                    <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">","</span>, header.Split(<span class="hljs-string">','</span>)
                                        .Select(a =&gt; <span class="hljs-string">"\""</span> + <span class="hljs-string">$"<span class="hljs-subst">{((JObject)Convert.ChangeType(row, <span class="hljs-keyword">typeof</span>(JObject))).GetValue(a)}</span>"</span>?
                                                        .Replace(Environment.NewLine, <span class="hljs-keyword">string</span>.Empty, StringComparison.OrdinalIgnoreCase)
                                                        .Replace(<span class="hljs-string">"\""</span>, <span class="hljs-string">"\"\""</span>, StringComparison.OrdinalIgnoreCase)
                                                        + <span class="hljs-string">"\""</span>
                                                        )));

            lines.AddRange(valueLines);
        }
        <span class="hljs-keyword">else</span>
        {
            <span class="hljs-keyword">var</span> props = items.FirstOrDefault().GetType().GetProperties();

            header = <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">","</span>, props.Select(x =&gt; x.Name));

            lines.Add(header);

            <span class="hljs-comment">//DO NOT include the starting and ending quotes inside the $ string interpolation.</span>
            <span class="hljs-keyword">var</span> valueLines = items.Select(row =&gt;
                                    <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">","</span>, header.Split(<span class="hljs-string">','</span>)
                                        .Select(a =&gt; <span class="hljs-string">"\""</span> + <span class="hljs-string">$"<span class="hljs-subst">{row.GetType().GetProperty(a).GetValue(row, <span class="hljs-literal">null</span>)}</span>"</span>?
                                                        .Replace(Environment.NewLine, <span class="hljs-keyword">string</span>.Empty, StringComparison.OrdinalIgnoreCase)
                                                        .Replace(<span class="hljs-string">"\""</span>, <span class="hljs-string">"\"\""</span>, StringComparison.OrdinalIgnoreCase)
                                                        + <span class="hljs-string">"\""</span>
                                                        )));

            lines.AddRange(valueLines);
        }

        <span class="hljs-keyword">var</span> csvString = <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">"\r\n"</span>, lines.ToArray());

        <span class="hljs-keyword">return</span> csvString;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.Empty;
    }
}
</code></pre>
<h2 id="heading-convert-any-ienumerable-to-datatable">Convert any IEnumerable to DataTable</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> DataTable <span class="hljs-title">ToDataTable</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> IEnumerable&lt;T&gt; items</span>)</span>
{
    DataTable dataTable = <span class="hljs-keyword">new</span> DataTable(<span class="hljs-keyword">typeof</span>(T).Name);

    PropertyInfo[] Props = <span class="hljs-keyword">typeof</span>(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    <span class="hljs-keyword">foreach</span> (PropertyInfo prop <span class="hljs-keyword">in</span> Props)
    {
        dataTable.Columns.Add(prop.Name);
    }
    <span class="hljs-keyword">foreach</span> (T item <span class="hljs-keyword">in</span> items)
    {
        <span class="hljs-keyword">var</span> values = <span class="hljs-keyword">new</span> <span class="hljs-keyword">object</span>[Props.Length];
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; Props.Length; i++)
        {
            values[i] = Props[i].GetValue(item, <span class="hljs-literal">null</span>);
        }
        dataTable.Rows.Add(values);
    }

    <span class="hljs-keyword">return</span> dataTable;
}
</code></pre>
<h1 id="heading-session-extensions">Session Extensions</h1>
<h2 id="heading-replace-a-value-in-a-session-key">Replace a value in a Session key</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Replace</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> ISession session, <span class="hljs-keyword">string</span> key, <span class="hljs-keyword">object</span> data</span>)</span>
{
    <span class="hljs-keyword">if</span> (session.Keys.Any(x =&gt; x.Equals(key, StringComparison.OrdinalIgnoreCase)))
    {
        session.Remove(key);
    }

    session.SetString(key, JsonConvert.SerializeObject(data));
}
</code></pre>
<h2 id="heading-get-value-from-a-session-key">Get value from a Session key</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">TryGet</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> ISession session, <span class="hljs-keyword">string</span> key, <span class="hljs-keyword">out</span> T data, <span class="hljs-keyword">bool</span> removeKey = <span class="hljs-literal">false</span></span>)</span>
{
    <span class="hljs-keyword">if</span> (session.Keys.Any(x =&gt; x.Equals(key, StringComparison.OrdinalIgnoreCase)))
    {
        <span class="hljs-keyword">var</span> objString = session.GetString(key);

        data = JsonConvert.DeserializeObject&lt;T&gt;(objString);

        <span class="hljs-keyword">if</span> (removeKey)
        {
            session.Remove(key);
        }

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    data = <span class="hljs-keyword">default</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
</code></pre>
<h1 id="heading-claimsprincipal-extensions">ClaimsPrincipal Extensions</h1>
<h2 id="heading-check-if-the-claims-has-a-particular-role">Check if the claims has a particular Role</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">HasRole</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> ClaimsPrincipal principal, <span class="hljs-keyword">string</span> role</span>)</span>
{
    <span class="hljs-keyword">return</span> principal.HasClaim(c =&gt;
                                    c.Type == ClaimTypes.Role &amp;&amp;
                                    c.Value.Equals(role, StringComparison.OrdinalIgnoreCase));
}
</code></pre>
<h2 id="heading-get-roles-from-the-claims">Get Roles from the claims</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IEnumerable&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">GetRoles</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> ClaimsPrincipal principal</span>)</span>
{
    <span class="hljs-keyword">return</span> principal.Claims.Where(c =&gt; c.Type == ClaimTypes.Role).Select(s =&gt; s.Value);
}
</code></pre>
<h2 id="heading-get-a-particular-claim-from-the-claims">Get a particular claim from the claims</h2>
<pre><code class="lang-csharp"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> T <span class="hljs-title">GetClaim</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> ClaimsPrincipal principal, <span class="hljs-keyword">string</span> claim</span>)</span>
 {
     <span class="hljs-keyword">var</span> result = principal?.Claims?.FirstOrDefault(f =&gt; f.Type.Equals(claim, StringComparison.OrdinalIgnoreCase))?.Value;

     <span class="hljs-keyword">return</span> (T)Convert.ChangeType(result, <span class="hljs-keyword">typeof</span>(T));
 }
</code></pre>
<h1 id="heading-datetime-extensions">DateTime Extensions</h1>
<h2 id="heading-convert-to-a-different-timezone">Convert to a different TimeZone</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> DateTime <span class="hljs-title">ConvertToTimeZone</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> DateTime dateTime, <span class="hljs-keyword">string</span> timeZoneId</span>)</span>
{
    <span class="hljs-keyword">return</span> TimeZoneInfo.ConvertTimeFromUtc(dateTime, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId));
}
</code></pre>
<h2 id="heading-check-if-a-given-datetime-is-between-a-time-range">Check if a given DateTime is between a time range</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsBetweenTimeRange</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> DateTime comparisonTime, DateTime? startDate, DateTime? endDate</span>)</span>
{
    <span class="hljs-keyword">return</span> (startDate.HasValue ? comparisonTime &gt;= startDate.Value : <span class="hljs-literal">true</span>) &amp;&amp; (endDate.HasValue ? comparisonTime &lt;= endDate.Value : <span class="hljs-literal">true</span>);
}
</code></pre>
<h2 id="heading-check-if-a-give-datetimeoffset-is-between-a-time-range">Check if a give DateTimeOffset is between a time range</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsBetweenTimeRange</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> DateTime comparisonTime, DateTimeOffset? startDate, DateTimeOffset? endDate</span>)</span>
{
    <span class="hljs-keyword">return</span> comparisonTime.IsBetweenTimeRange(startDate.HasValue ? startDate.Value.DateTime : <span class="hljs-literal">null</span>, endDate.HasValue ? endDate.Value.DateTime : <span class="hljs-literal">null</span>);
}
</code></pre>
<h2 id="heading-convert-to-a-datetimeoffset-based-on-a-timezone">Convert to a DateTimeOffset based on a TimeZone</h2>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> DateTimeOffset? ToDateTimeOffset(<span class="hljs-keyword">this</span> DateTime? dateTime, <span class="hljs-keyword">string</span> timeZoneId)
{
    <span class="hljs-keyword">return</span> dateTime.HasValue ? <span class="hljs-keyword">new</span> DateTimeOffset(dateTime.Value, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId).BaseUtcOffset) : <span class="hljs-literal">null</span>;
}
</code></pre>
<h1 id="heading-enum-extensions">Enum Extensions</h1>
<h2 id="heading-get-display-name-of-an-enum">Get display name of an Enum</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetDisplayName</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> Enum enumValue</span>)</span>
{
    <span class="hljs-keyword">var</span> displayName = enumValue.GetType().GetMember(enumValue.ToString()).FirstOrDefault().GetCustomAttribute&lt;DisplayAttribute&gt;()?.GetName();

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(displayName) ? enumValue.ToString() : displayName;
}
</code></pre>
<h1 id="heading-httpresponsemessage-extensions">HTTPResponseMessage Extensions</h1>
<h2 id="heading-ensure-the-httpresponsemessage-has-a-success-status-code-and-log-failures">Ensure the HTTPResponseMessage has a Success Status code and log failures</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">EnsureSuccessStatusCodeAndLogFailures</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> HttpResponseMessage httpResponseMessage, ILogger logger</span>)</span>
{
    <span class="hljs-keyword">if</span> (httpResponseMessage == <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(httpResponseMessage));
    }
    <span class="hljs-keyword">if</span> (logger == <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(logger));
    }

    <span class="hljs-keyword">if</span> (!httpResponseMessage.IsSuccessStatusCode)
    {
        <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> httpResponseMessage.Content?.ReadAsStringAsync();

        <span class="hljs-keyword">var</span> requestUri = httpResponseMessage.RequestMessage?.RequestUri;

        logger.LogError(<span class="hljs-string">$"HTTP call to <span class="hljs-subst">{requestUri}</span> returned status code <span class="hljs-subst">{httpResponseMessage.StatusCode}</span> with response <span class="hljs-subst">{response}</span>"</span>);
    }

    httpResponseMessage.EnsureSuccessStatusCode();
}
</code></pre>
<h1 id="heading-tokenresponse-extensions">TokenResponse Extensions</h1>
<h2 id="heading-ensure-the-tokenresponse-has-a-success-status-and-log-failures">Ensure the TokenResponse has a Success Status and log failures</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">CheckResponseAndLogFailures</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> TokenResponse tokenResponse, ILogger logger</span>)</span>
{
    <span class="hljs-keyword">if</span> (tokenResponse == <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(tokenResponse));
    }
    <span class="hljs-keyword">if</span> (logger == <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(logger));
    }

    <span class="hljs-keyword">if</span> (tokenResponse.IsError)
    {
        <span class="hljs-keyword">var</span> response = JsonConvert.SerializeObject(tokenResponse);

        logger.LogError(<span class="hljs-string">"Access Token call to returned with error and data: {data}"</span>, response);
    }
}
</code></pre>
<h1 id="heading-string-extensions">String Extensions</h1>
<h2 id="heading-slugify">Slugify</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Slugify</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span> phrase</span>)</span>
{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(phrase))
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.Empty;
    }

    <span class="hljs-keyword">var</span> output = phrase.RemoveAccents().ToLower();

    output = Regex.Replace(output, <span class="hljs-string">@"[^A-Za-z0-9\s-]"</span>, <span class="hljs-string">""</span>);

    output = Regex.Replace(output, <span class="hljs-string">@"\s+"</span>, <span class="hljs-string">" "</span>).Trim();

    output = Regex.Replace(output, <span class="hljs-string">@"\s"</span>, <span class="hljs-string">"-"</span>);

    <span class="hljs-keyword">return</span> output;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">RemoveAccents</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span> text</span>)</span>
{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(text))
        <span class="hljs-keyword">return</span> text;

    text = text.Normalize(NormalizationForm.FormD);
    <span class="hljs-keyword">char</span>[] chars = text
        .Where(c =&gt; CharUnicodeInfo.GetUnicodeCategory(c)
        != UnicodeCategory.NonSpacingMark).ToArray();

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>(chars).Normalize(NormalizationForm.FormC);
}
</code></pre>
<h2 id="heading-check-if-a-email-is-valid">Check if a email is valid</h2>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidEmail</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span> email</span>)</span>
{
    <span class="hljs-keyword">var</span> isValid = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-keyword">var</span> emailAddress = <span class="hljs-keyword">new</span> MailAddress(email.Trim());
    }
    <span class="hljs-keyword">catch</span>
    {
        isValid = <span class="hljs-literal">false</span>;
    }

    <span class="hljs-keyword">return</span> isValid;
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[ASP.Net Core Feature Flags - Intro]]></title><description><![CDATA[ASP.Net Core is a free, open-source framework for building web applications and APIs. One of its powerful features is the ability to use feature flags, which allow developers to enable or disable certain functionality in their application without hav...]]></description><link>https://blogs.aspnet.in/aspnet-core-feature-flags-intro</link><guid isPermaLink="true">https://blogs.aspnet.in/aspnet-core-feature-flags-intro</guid><category><![CDATA[asp.net core]]></category><category><![CDATA[  feature flags]]></category><category><![CDATA[Feature Management]]></category><category><![CDATA[feature release]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Fri, 20 Jan 2023 17:21:08 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="http://ASP.Net">ASP.Net</a> Core is a free, open-source framework for building web applications and APIs. One of its powerful features is the ability to use feature flags, which allow developers to enable or disable certain functionality in their application without having to deploy new code.</p>
<h1 id="heading-what-is-a-feature-flag">What is a Feature Flag?</h1>
<p>A feature flag, also known as a feature toggle or a feature switch, is a simple mechanism that allows developers to control the behavior of their applications at runtime. By using feature flags, developers can easily test new functionality in a production environment without affecting the entire application. This can be especially useful for testing new features with a small subset of users before rolling them out to the entire user base.</p>
<h2 id="heading-implementing-feature-flags-in-aspnethttpaspnet-core">Implementing Feature Flags in <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core</h2>
<p>Implementing feature flags in an <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core application is easy. You can use the Microsoft.FeatureManagement package, which is available on NuGet, to add feature flags to your application. This package provides a simple API that allows you to configure and manage feature flags in your application.</p>
<p>Here is an example of how to use feature flags in an <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core application:</p>
<p>1.Install the Microsoft.FeatureManagement package by running the following command in the package manager console:</p>
<pre><code class="lang-plaintext">Install-Package Microsoft.FeatureManagement
</code></pre>
<p>2.In the <code>Startup.cs</code> file, add the following line to the <code>ConfigureServices</code> method to enable feature management:</p>
<pre><code class="lang-csharp">services.AddFeatureManagement();
</code></pre>
<p>3.Create a new class to represent your feature flag. For example, if you are creating a feature flag for a new feature called "NewFeature", you would create a class like this:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">NewFeature</span> : <span class="hljs-title">IFeature</span>
{
}
</code></pre>
<p>4.In the <code>Startup.cs</code> file, add the following line to the <code>Configure</code> method to enable the feature flag:</p>
<pre><code class="lang-csharp">app.UseFeatureManagement();
</code></pre>
<p>5.In your controller or service, you can now use the <code>[FeatureGate]</code> attribute to enable or disable the feature based on the flag. For example, if you want to enable the "NewFeature" feature flag for a specific action in your controller, you would add the following attribute:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">FeatureGate(NewFeature)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">MyAction</span>(<span class="hljs-params"></span>)</span>
{
<span class="hljs-comment">// Code for the action goes here</span>
}
</code></pre>
<p>This is just a basic example of how to use feature flags in an <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core application. With the Microsoft.FeatureManagement package, you can also configure feature flags using JSON files, environment variables, or a custom provider. You can also use the package to implement advanced features such as time-based rollouts and user-based targeting.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Feature flags are a powerful tool that can help you improve the development and deployment process of your <a target="_blank" href="http://ASP.Net">ASP.Net</a> Core application. By using feature flags, you can easily test new functionality in a production environment without affecting the entire application. This can save you time and resources and help you deliver new features to your users faster.</p>
]]></content:encoded></item><item><title><![CDATA[Attribute-Based Access Control Sample for a .Net Core API Application]]></title><description><![CDATA[GitHub Link: https://github.com/venbacodes/ABAC-Sample-for-API
This is a simple demo sample on implementing ABAC in a .Net Core API Application. This uses three main aspects of Attributes => ACCESS, SCOPE, and MODULE.
Key Points

Access, Scope, and M...]]></description><link>https://blogs.aspnet.in/attribute-based-access-control-sample-for-a-net-core-api-application</link><guid isPermaLink="true">https://blogs.aspnet.in/attribute-based-access-control-sample-for-a-net-core-api-application</guid><category><![CDATA[dotnetcore]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[authentication]]></category><category><![CDATA[authorization]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Sun, 02 Jan 2022 21:08:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1641157678037/ouRnX5tGo.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitHub Link: https://github.com/venbacodes/ABAC-Sample-for-API</p>
<p>This is a simple demo sample on implementing ABAC in a .Net Core API Application. This uses three main aspects of Attributes =&gt; ACCESS, SCOPE, and MODULE.</p>
<h2 id="heading-key-points">Key Points</h2>
<ol>
<li>Access, Scope, and Module attributes are used</li>
<li>It is possible to extend this sample to accommodate as many attributes as needed</li>
<li>No External libraries were used</li>
<li>Handled all the necessary authn and authz in the handlers itself.</li>
<li>Optional takeaway - added an additional path for restricting resources in <a target="_blank" href="https://github.com/venbacodes/ABAC-Sample-for-API/blob/main/Authorization/PermissionsAuthHandler.cs#L60">PermissionsAuthHandler.cs#L60</a></li>
</ol>
<h2 id="heading-to-explore">To Explore</h2>
<ol>
<li>Clone and run the code</li>
<li>Generate a JWT token with email/sub and exp. Applicable emails can be found in <a target="_blank" href="https://github.com/venbacodes/ABAC-Sample-for-API/blob/main/Model/TestUsers.cs">TestUsers.cs</a></li>
<li>Add the generated JWT token in the swagger authorization menu and call the APIs</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[How to setup Azure App Configuration and Key Vault in a .Net Core project]]></title><description><![CDATA[This is a basic "how-to document" blog on configuring Azure App Configuration and Key Vault in a .Net Core project. 
Topics covered:

Setup Azure App Configuration
Setup Azure Key Vault
Configuring a .Net Core project with sample code
Setting up acce...]]></description><link>https://blogs.aspnet.in/how-to-setup-azure-app-configuration-and-key-vault-in-a-net-core-project</link><guid isPermaLink="true">https://blogs.aspnet.in/how-to-setup-azure-app-configuration-and-key-vault-in-a-net-core-project</guid><category><![CDATA[Azure]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Thu, 16 Dec 2021 19:22:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1639682460051/YE_dd9A0Z.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a basic "how-to document" blog on configuring Azure App Configuration and Key Vault in a .Net Core project. 
Topics covered:</p>
<ul>
<li>Setup Azure App Configuration</li>
<li>Setup Azure Key Vault</li>
<li>Configuring a .Net Core project with sample code<ul>
<li>Setting up access policies in Key Vault</li>
<li>Authenticating your application code to fetch secrets from Key Vault</li>
</ul>
</li>
</ul>
<p>Disclaimer: Probably, you could have seen this how-to information in the MSFT official documentation. But this blog will give you the curated steps removing all the clutters.</p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<p>Make sure you have access to do the below in Azure,</p>
<ol>
<li>Create Azure App Configuration</li>
<li>Create Azure Key Vault</li>
<li>Edit Access policy in the newly created Key Vault</li>
<li>Enable System assigned managed identity for the Application hosted in Azure</li>
<li>Register an app in Azure Active Directory (optional)</li>
</ol>
<h1 id="heading-intro-on-the-said-azure-jargons">Intro on the said Azure jargons</h1>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-app-configuration/">Azure App Configuration</a> lets you maintain the config details of your app in Azure so that your app can be free of any environment dependency. It has labels that can be used to maintain values of different environments for a single config property.</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/general">Key Vault</a> is a resource in Azure to store secret information that can be accessed by your apps through managed identities or service principal authentication.</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/">Managed Identities</a> are used to authenticate across azure resources through an identity thereby eliminating the need to manage credentials. In this blog, we will use the <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview#managed-identity-types">System-assigned managed identity</a> of an Azure App Service which we will enable in it to access the secrets in a Key Vault.</p>
<h1 id="heading-create-azure-app-configuration">Create Azure App Configuration</h1>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core5x#create-an-app-configuration-store">this</a> to create a new Azure App Configuration resource and add your config properties to the store.</p>
<p><code>Configuration Explorer</code> under <code>Operations</code>, is where the config properties can be seen and edited.</p>
<p>Every key has options to add more values with different labels which can be used for different environments. Here is a screenshot of the Configuration Explorer displaying 2 values for a single key with one having a label and another empty.</p>
<p><img src="https://user-images.githubusercontent.com/5276778/146384520-866bca50-5918-4479-99dc-a61327011739.png" alt="image" /></p>
<p>To retrieve these values in an application, <code>Connection String</code> that is uniquely generated under <code>Access Keys</code> is needed. Get the connection string from this page which will be used in our application to connect to the App Configuration store.</p>
<h1 id="heading-create-azure-key-vault">Create Azure Key Vault</h1>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/general/quick-create-portal#create-a-vault">this</a> to create a new Azure Key Vault resource. For this blog, we will look into secrets alone to store our passwords/client secrets. Check <a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-portal">this</a> on how to create a secret in the Key Vault.</p>
<p>Retrieving secrets' value from the Key Vault can be done using the <code>Azure.Identity</code> package. But if you want to load the secrets in the <code>ConfigurationSection</code>, then there is a way in the <code>ConfigurationBuilder</code> to include both App Configuration key-values and Key Vault secrets.</p>
<p>To do this, the secrets are to be referenced in the App Configuration as a new key-value. Check <a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-app-configuration/use-key-vault-references-dotnet-core?tabs=core5x#add-a-key-vault-reference-to-app-configuration">this</a> article on how to add a KV reference in the App Configuration.</p>
<p>App Configuration will use the identity that is used to load itself for accessing the secrets in the Key Vault. This is something we will set up in the next steps. As of now, your App Configuration should look something like the below screenshot,</p>
<p><img src="https://user-images.githubusercontent.com/5276778/146396433-b48d387b-bbb2-4e96-9849-697f667277cd.png" alt="image" /></p>
<h1 id="heading-configuring-a-net-core-project-with-sample-code">Configuring a .Net Core project with sample code</h1>
<p>Here is the sample app in GitHub.</p>
<p>https://github.com/venbacodes/AppConfigKVSample</p>
<p>To connect with the Azure App Configuration, you will need the package <code>Microsoft.Extensions.Configuration.AzureAppConfiguration</code> which has the extension method <code>AddAzureAppConfiguration</code> for the <code>ConfigurationBuilder</code>. This extension method will get the Connection String of the App Configuration and can also configure the Key Vault using the <code>ConfigureKeyVault</code> method in its options.</p>
<pre><code>builder.Host.ConfigureAppConfiguration((webHostBuilderContext, config) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
{
    <span class="hljs-keyword">var</span> isDevelopment <span class="hljs-operator">=</span> webHostBuilderContext.HostingEnvironment.IsDevelopment();
    <span class="hljs-keyword">var</span> environment <span class="hljs-operator">=</span> webHostBuilderContext.HostingEnvironment.EnvironmentName.ToLower();

    config
    .AddJsonFile(<span class="hljs-string">"appsettings.json"</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>)
    .AddJsonFile($<span class="hljs-string">"appsettings.{webHostBuilderContext.HostingEnvironment.EnvironmentName}.json"</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);

    <span class="hljs-keyword">var</span> settings <span class="hljs-operator">=</span> config.Build();

    <span class="hljs-keyword">var</span> appConfigConnectionString <span class="hljs-operator">=</span> settings.GetSection(AppConfigConnectionStringSection);

    <span class="hljs-keyword">if</span> (appConfigConnectionString <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> <span class="hljs-operator">!</span><span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(appConfigConnectionString.Value))
    {
        config.AddAzureAppConfiguration(options <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
        {
            options
            .Connect(appConfigConnectionString.Value)
            .ConfigureKeyVault(kv <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
            {
                <span class="hljs-keyword">if</span> (isDevelopment)
                {
                                    <span class="hljs-comment">///You WON'T need this if you are logged into Visual Studio and your account has been setup </span>
                                    <span class="hljs-comment">///with access policy in the Key Vault you are trying to connect. You can just use the DefaultAzureCredential</span>
                                    <span class="hljs-comment">///that is set in the else part.</span>
                                    <span class="hljs-keyword">var</span> cred <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ClientSecretCredential(
                        settings.GetSection(<span class="hljs-string">"DevCredential:TenantId"</span>).Value,
                        settings.GetSection(<span class="hljs-string">"DevCredential:ClientId"</span>).Value,
                        settings.GetSection(<span class="hljs-string">"DevCredential:ClientSecret"</span>).Value);

                    kv.SetCredential(cred);
                }
                <span class="hljs-keyword">else</span>
                {
                    kv.SetCredential(<span class="hljs-keyword">new</span> DefaultAzureCredential());
                }
            })
            .Select(KeyFilter.Any, LabelFilter.Null)
            .Select(KeyFilter.Any, environment ?? LabelFilter.Null);
        });
    }
});
</code></pre><p>OK, here are the explanations on the above code,</p>
<p>This reads the app config connection string we have in our appsettings.json file</p>
<pre><code><span class="hljs-keyword">var</span> appConfigConnectionString <span class="hljs-operator">=</span> settings.GetSection(AppConfigConnectionStringSection);
</code></pre><p>We will now use this connection string in the AddAzureAppConfiguration method,</p>
<pre><code>options
.Connect(appConfigConnectionString.Value)
</code></pre><p>Now, we are configuring the Key Vault in the config builder to load its secrets also as configuration into the application's Configuration.</p>
<pre><code>.ConfigureKeyVault(kv <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-comment">///Code removed for simplicity</span>
})
</code></pre><p>We are trying to connect to the Key Vault using our registered identity in AD for development and the default credential when not in development. We will see about this more in the next section.</p>
<p>While getting the data from the app config, we are actually filtering the keys and values based on the environment.</p>
<pre><code>.Select(KeyFilter.Any, LabelFilter.Null)
.Select(KeyFilter.Any, environment ?? LabelFilter.Null)
</code></pre><p>The above code tries to filter the values based on the label filter. Actually, this will include all keys without any labels and all keys with the specified label and finally will overwrite the values in the first filter. The order of filters is important here.</p>
<h1 id="heading-access-policies-in-key-vault">Access Policies in Key Vault</h1>
<p>Access policies section in the Key Vault is where we will assign permissions for applications and users using the managed identities that are formed from the Azure Active Directory.</p>
<p>Here is a screenshot of this section,
<img src="https://user-images.githubusercontent.com/5276778/146415530-25996094-3dd3-40a4-8f3b-2f9f87779eec.png" alt="image" /></p>
<h2 id="heading-azure-ad-identities">Azure AD Identities</h2>
<p>You can either use an Azure App Services' System-assigned managed identity or your azure account's identity to connect to the Key Vault. I recommend using your own azure account to authenticate and connect to the Key Vault by logging into Visual Studio and using <code>kv.SetCredential(new DefaultAzureCredential());</code></p>
<p>If you don't want to use your azure account, there is another possibility to register an App in the Azure AD and use its identity in this Access Policy.</p>
<h3 id="heading-app-registration-in-azure-ad">App Registration in Azure AD</h3>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app">this</a> to register an application in the Azure AD and get the below details from it to set the credentials while configuring the KV.</p>
<pre><code>TenantId <span class="hljs-operator">-</span> This will be the common id <span class="hljs-keyword">for</span> your azure tenant
ClientId <span class="hljs-operator">-</span> This will be the unique id <span class="hljs-keyword">for</span> the registered app
ClientSecret <span class="hljs-operator">-</span> You have to create <span class="hljs-built_in">this</span> secret after registering the app. This <span class="hljs-keyword">is</span> covered in the below link
</code></pre><p>https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-a-client-secret</p>
<h3 id="heading-azure-app-service-system-assigned-managed-identity">Azure App Service System Assigned Managed Identity</h3>
<p>To use the Azure App Services' identity, you would have to enable it as like below,</p>
<p><img src="https://user-images.githubusercontent.com/5276778/146416998-4704e9b0-215e-4f83-9110-6f10124e57e6.png" alt="image" /></p>
<p>Once this is done, <code>kv.SetCredential(new DefaultAzureCredential());</code> will automatically use the app's identity and try to authenticate and get values from the KV.</p>
<p>To complete a successful authentication, add this newly registered app in the access policy section under the KV as a principal and give access to read secrets.</p>
<h1 id="heading-finally-seeing-the-values-from-the-app-config">Finally seeing the values from the app config</h1>
<p>After completing the above steps, you can now access the Configuration with the <code>key</code> name <code>ACDemo</code> which I used in the App Configuration. For this demo, I have read the key during startup and added it to a singleton service so that I can get them injected in all places.</p>
<pre><code><span class="hljs-keyword">var</span> acDemo <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ACDemo();
builder.Configuration.Bind(<span class="hljs-string">"ACDemo"</span>, acDemo);

builder.Services.AddSingleton(acDemo);
</code></pre>]]></content:encoded></item><item><title><![CDATA[Using counters in Azure DevOps pipeline to increment assembly version numbers]]></title><description><![CDATA[There are many pipeline task extensions in the Azure Marketplace which can be installed directly to your DevOps instance. But for funny 😁 reasons, I want to do this myself where I ended up using the counter function and found it satisfied.
I hope yo...]]></description><link>https://blogs.aspnet.in/using-counters-in-azure-devops-pipeline-to-increment-assembly-version-numbers</link><guid isPermaLink="true">https://blogs.aspnet.in/using-counters-in-azure-devops-pipeline-to-increment-assembly-version-numbers</guid><category><![CDATA[Azure]]></category><category><![CDATA[Devops]]></category><category><![CDATA[build]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Fri, 10 Dec 2021 15:13:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1639149130454/xEPf17zF-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are <a target="_blank" href="https://marketplace.visualstudio.com/search?term=versioning&amp;target=AzureDevOps&amp;category=All%20categories&amp;sortBy=Relevance">many pipeline task extensions</a> in the Azure Marketplace which can be installed directly to your DevOps instance. But for funny 😁 reasons, I want to do this myself where I ended up using the counter function and found it satisfied.</p>
<p>I hope you also will find this helpful 🎉🎉🎉.</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#counter">Counter</a> function maintains a seed value that should be variable in your pipeline and will increment it on each pipeline run.</p>
<p>So, here is my complete pipeline yml file before going further,</p>
<pre><code><span class="hljs-attribute">name</span>: '$(Build.DefinitionName)_$(SourceBranchName)_$(Year:yyyy).$(Month).$(DayOfMonth).$(revision)'

<span class="yaml"><span class="hljs-attr">trigger:</span>
    <span class="hljs-attr">branches:</span>
        <span class="hljs-attr">include:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">develop</span>    

<span class="hljs-attr">pool:</span>
  <span class="hljs-attr">vmImage:</span> <span class="hljs-string">'windows-latest'</span>

<span class="hljs-attr">variables:</span>
  <span class="hljs-attr">solution:</span> <span class="hljs-string">'*/.sln'</span>
  <span class="hljs-attr">buildPlatform:</span> <span class="hljs-string">'Any CPU'</span>
  <span class="hljs-attr">buildConfiguration:</span> <span class="hljs-string">'Release'</span>  
  <span class="hljs-attr">buildNumber:</span> <span class="hljs-string">'Will be set dynamically'</span>
  <span class="hljs-attr">revision:</span> <span class="hljs-string">$[counter(format('{0:dd}',</span> <span class="hljs-string">pipeline.startTime),</span> <span class="hljs-number">1</span><span class="hljs-string">)]</span>

<span class="hljs-attr">steps:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">PowerShell@2</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">'Preparing Build Number'</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">targetType:</span> <span class="hljs-string">'inline'</span>
    <span class="hljs-attr">script:</span> <span class="hljs-string">|
      $currentDate = $(Get-Date)
      $year = $currentDate.Year
      $month = $currentDate.Month
      $day = $currentDate.Day
      Write-Host $currentDate
      Write-Host $day
      Write-Host $env:revision
      Write-Host "##vso[task.setvariable variable=buildNumber]$year.$month.$day.$env:revision"
</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">UseDotNet@2</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">'Use .NET 6'</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">packageType:</span> <span class="hljs-string">'sdk'</span>
    <span class="hljs-attr">version:</span> <span class="hljs-string">'6.0.x'</span>
    <span class="hljs-attr">includePreviewVersions:</span> <span class="hljs-literal">true</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">NuGetToolInstaller@1</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">'Update Nuget'</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">checkLatest:</span> <span class="hljs-literal">true</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">NuGetCommand@2</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">'NuGet restore'</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">'restore'</span>
    <span class="hljs-attr">restoreSolution:</span> <span class="hljs-string">'$(solution)'</span>
    <span class="hljs-attr">feedsToUse:</span> <span class="hljs-string">config</span>
    <span class="hljs-attr">nugetConfigPath:</span> <span class="hljs-string">nuget.config</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">DotNetCoreCLI@2</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">Build</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">projects:</span> <span class="hljs-string">'ProjectName.csproj'</span>
    <span class="hljs-attr">arguments:</span> <span class="hljs-string">'--configuration $(buildConfiguration) /p:AssemblyVersion=$(buildNumber)'</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">DotNetCoreCLI@2</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">Publish</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">publish</span>
    <span class="hljs-attr">publishWebProjects:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">projects:</span> <span class="hljs-string">'ProjectName.csproj'</span>
    <span class="hljs-attr">arguments:</span> <span class="hljs-string">'--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory) /p:Version=$(buildNumber)'</span>
    <span class="hljs-attr">zipAfterPublish:</span> <span class="hljs-literal">True</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">task:</span> <span class="hljs-string">PublishBuildArtifacts@1</span>
  <span class="hljs-attr">displayName:</span> <span class="hljs-string">'Publish Artifact'</span>
  <span class="hljs-attr">inputs:</span>
    <span class="hljs-attr">PathtoPublish:</span> <span class="hljs-string">'$(build.artifactstagingdirectory)'</span>
  <span class="hljs-attr">condition:</span> <span class="hljs-string">succeededOrFailed()</span></span>
</code></pre><p>We will focus only on the <code>variables section</code> and the <code>PowerShell task</code> in this blog.</p>
<p>In the <code>variables section</code>, I have added a variable <code>revision</code> that is set from the counter function.</p>
<p><code>revision: $[counter(format('{0:dd}', pipeline.startTime), 1)]</code></p>
<p>This counter function is using <code>startTime</code> of the pipeline in the format of day number as its seed. The counter starts from 1.</p>
<p>On each pipeline run, the counter will check for changes in the day number in the pipeline.startTime, and if it's new, the counter will reset to the default value which is 1. </p>
<p>As of now, we have achieved a revision number that gets incremented on each pipeline run on the same day and also resets to 1 on the next day.</p>
<p>Now, in the <code>PowerShell task</code>, we are going to use this revision to form a build number for our assembly.</p>
<p><code>Write-Host "##vso[task.setvariable variable=buildNumber]$year.$month.$day.$env:revision"</code></p>
<p>We are trying to achieve a build number in this format - <code>yyyy.mm.dd.revision</code> =&gt; <code>2021.12.26.13</code></p>
<p>In the above Write-Host command, we are not printing any values to the output console, instead, we are using the <a target="_blank" href="https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?view=azure-devops&amp;tabs=bash#setvariable-initialize-or-modify-the-value-of-a-variable">logging command #vso to set the variable</a> <code>buildNumber</code> with our required value.</p>
<p>Finally, in the <code>build</code> and <code>publish</code> tasks, I passed the <code>buildNumber</code> to the <code>/p:AssemblyVersion</code> and <code>/p:Version</code> properties respectively.</p>
<p>You can write your own logic to set the <code>buildNumber</code> using the counter function and achieve build numbers in any format you want.</p>
<p>Follow me on the below places for more of my blog posts,</p>
<p><a target="_blank" href="https://dev.to/vengi83644">dev.to</a></p>
<p><a target="_blank" href="https://blogs.aspnet.in">hashnode</a></p>
<p><a target="_blank" href="https://vengi83644.medium.com">Medium</a></p>
]]></content:encoded></item><item><title><![CDATA[Impersonation in a .Net core application with Identity Server 4]]></title><description><![CDATA[Identity Server 4 does not provide native impersonation support which I needed for one of my projects. I have implemented one on top of it.
Here is the sample project I did on GitHub.
https://github.com/venbacodes/ImpersonationSample-IdentityServer4
...]]></description><link>https://blogs.aspnet.in/impersonation-in-a-net-core-application-with-identity-server-4</link><guid isPermaLink="true">https://blogs.aspnet.in/impersonation-in-a-net-core-application-with-identity-server-4</guid><category><![CDATA[server]]></category><category><![CDATA[authentication]]></category><category><![CDATA[authorization]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Wed, 08 Dec 2021 07:07:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638947191821/fXCFhe7A-.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Identity Server 4 does not provide native impersonation support which I needed for one of my projects. I have implemented one on top of it.</p>
<p>Here is the sample project I did on GitHub.</p>
<p>https://github.com/venbacodes/ImpersonationSample-IdentityServer4</p>
<p>This is a sample application to show a way to implement impersonation when using Identity Server.</p>
<p>Key Points</p>
<ol>
<li>Authorization policy has been set up to restrict impersonation to users with specific roles.</li>
<li>Admin users' email is added as a claim while impersonating so that it can be used while ending the impersonation.</li>
<li>Logic is simple as authenticating with the victim users' email for impersonation with additional claims to track the impersonation and the impersonating user.</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Using Azure automation and Events to monitor a parent Key Vault and update the secret in child Key Vault(s)]]></title><description><![CDATA[Before going into the actual blog, I wanted to explain the Azure resources that we are going to discuss in simple words,
Key Vault is a resource in Azure to store secret information that can be accessed by your apps through managed identities or serv...]]></description><link>https://blogs.aspnet.in/using-azure-automation-and-events-to-monitor-a-parent-key-vault-and-update-the-secret-in-child-key-vaults</link><guid isPermaLink="true">https://blogs.aspnet.in/using-azure-automation-and-events-to-monitor-a-parent-key-vault-and-update-the-secret-in-child-key-vaults</guid><category><![CDATA[Azure]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[automation]]></category><category><![CDATA[events]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Fri, 03 Dec 2021 14:49:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638544026050/VceLaP_nA.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638543987497/43cu5qkpa.png" alt="image.png" /></p>
<p>Before going into the actual blog, I wanted to explain the Azure resources that we are going to discuss in simple words,</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/general">Key Vault</a> is a resource in Azure to store secret information that can be accessed by your apps through managed identities or service principal authentication.</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/automation">Azure Automation</a> is a service that gives you the ability to automate tasks, do configuration changes, do updates to your azure resources using Az Cmds through runbooks in it. So, practically, we can run any automation using the Az cmds.</p>
<p><a target="_blank" href="https://docs.microsoft.com/en-us/azure/event-grid">Azure Event Grid</a> is a service that fires events to your chosen event handlers or webhooks for the events that happen in your Azure resources. In this blog, we are going to have KVs as the event source and Webhooks as the handler.</p>
<p>Now, in this blog, I am going to explain how you can use Azure Automation to detect changes in a KV and update the secret value in another KV. </p>
<p>Why would someone do that? OK, consider having 100s of KVs in Azure and are being used by 100s of apps. If you have a secret value that is in all these KVs and you don't want your apps connecting to multiple KVs, then it's muscle work to update the secret value in that many KVs.</p>
<p>So, Azure Automation and Event Grid subscriptions come in to help us detect the new version of the secret in a KV and update its value in the other KVs.</p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<p>Make sure, you have access to do the below in Azure,</p>
<ol>
<li>Create Automation resource</li>
<li>Create Key Vault</li>
<li>Create Events in Key Vault</li>
<li>Edit Access Policies in Key Vault</li>
</ol>
<h2 id="heading-step-1-create-azure-automation">Step 1: Create Azure Automation</h2>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/automation/quickstarts/create-account-portal">this</a> to create a new Azure Automation account with System assigned identity. We will use this system assigned identity to access the secrets in the KV.</p>
<p>By default, the Azure Key Vault module is enabled in the Azure Automation. Make sure you have it in the Modules section. I am using the Runtime version 5.1 for this blog.</p>
<p><img src="https://user-images.githubusercontent.com/5276778/144612469-991f3faf-2271-40b8-98b8-1daea709bfd6.png" alt="image" /></p>
<h2 id="heading-step-2-create-key-vaults">Step 2: Create Key Vaults</h2>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/general/quick-create-portal#create-a-vault">this</a> to create a new KV. For this blog, I am creating 2 Key vaults - 1 for parent and 1 for children. I want to update the secret in child KVs when there is a new version of the secret in the parent.</p>
<p>Both the parent and child KVs needs to be set up with Access policies including the Automation account we created in Step 1 as like in the below pic,</p>
<p><img src="https://user-images.githubusercontent.com/5276778/144611707-a5b2eb2d-dfe0-49e5-8429-e86c01185ab3.png" alt="image" /></p>
<p>Populate the KVs with secrets in it by following <a target="_blank" href="https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-portal#add-a-secret-to-key-vault">this</a>. </p>
<h2 id="heading-step-3-create-runbook-in-the-azure-automation-account">Step 3: Create Runbook in the Azure Automation account</h2>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/automation/learn/automation-tutorial-runbook-textual#create-new-runbook">this</a> to create a new Runbook in the Azure Automation account. For this blog, we will use the PowerShell workflow runbook and we will use the Az cmds.</p>
<p>Follow <a target="_blank" href="https://docs.microsoft.com/en-us/azure/automation/automation-webhooks#from-the-portal">this</a> to create a Webhook under the newly created Runbook. Make sure you copy the Webhook URL while creating, as it cannot be retrieved later. We will need this URL to be added to the KV's Event Handler.</p>
<h2 id="heading-step-4-setup-event-subscription-and-handlers-in-parent-kv">Step 4: Setup Event Subscription and handlers in Parent KV</h2>
<ol>
<li>Go to your parent KV.</li>
<li>Select Events from the left section.</li>
<li>Create an Event Subscription.</li>
<li>Give the subscription and the topic unique names.</li>
<li>For the <strong>Event Types</strong>, choose "Secret New Version Created" as this is what we are going to monitor in this blog.</li>
<li>For the <strong>Endpoint Type</strong>, choose "WebHook" and type in the URL we got from Step 3 for the endpoint.</li>
<li>Click on create to save the Event Subscription</li>
</ol>
<p>As of now, we have subscribed to the new secret version create an event in the KV, and set Runbook's Webhook as the event handler.</p>
<h2 id="heading-step-5-catch-the-event-data-in-the-azure-automation-runbook">Step 5: Catch the event data in the Azure Automation Runbook</h2>
<p>In this step, we will catch the event data in the webhook as use Az cmds for setting the secret value in our child KV. </p>
<p>Go to your Runbook's Edit window and type in the below PS code in it.</p>
<pre><code>param
(
[Parameter (Mandatory = $false)]
[<span class="hljs-keyword">object</span>] $WebhookData
)

<span class="hljs-keyword">if</span> ($WebhookData) 
{
    Write-Output $WebhookData.RequestBody

    $bodyJson =  $WebhookData.RequestBody | ConvertFrom-Json <span class="hljs-comment"># Converting to json for easier manipulation</span>

    <span class="hljs-comment"># Getting the KV's name, Secret's name and the Event Type</span>
    $secretName = $bodyJson[<span class="hljs-number">0</span>].data.ObjectName
    $parentKv = $bodyJson[<span class="hljs-number">0</span>].data.VaultName    
    $eventType = $bodyJson[<span class="hljs-number">0</span>].eventType
    $childKv = <span class="hljs-string">""</span>

    <span class="hljs-keyword">if</span>($eventType -eq <span class="hljs-string">"Microsoft.KeyVault.SecretNewVersionCreated"</span>)
    {
        <span class="hljs-comment"># Authenticate using the System assigned identity of the Azure Automation account</span>
        Connect-AzAccount -Identity

        <span class="hljs-comment"># Using Az cmd for getting the secret's latest version value from the KV</span>
        $secretValue = Get-AzKeyVaultSecret -VaultName $parentKv -Name $secretName -AsPlainText

        Write-Output <span class="hljs-string">"A new version for secret $(<span class="hljs-subst">$secretName</span>) has been created in KV - $(<span class="hljs-subst">$parentKv</span>) with value - $(<span class="hljs-subst">$secretValue</span>)"</span>

        <span class="hljs-keyword">if</span>($secretName -eq <span class="hljs-string">"secret-child-1"</span>)
        {
            $childKv = <span class="hljs-string">"child-1-kv"</span>           
        }
        <span class="hljs-keyword">elseif</span>($secretName -eq <span class="hljs-string">"secret-child-2"</span>)
        {
            $childKv = <span class="hljs-string">"child-2-kv"</span>
        }

        <span class="hljs-keyword">if</span>($childKv -eq <span class="hljs-string">""</span>)
        {
            Write-Output <span class="hljs-string">"No secrets have been set in the child KVs as the updated key in parent is not associated with the children"</span>
        }
        <span class="hljs-keyword">else</span>
        {
            <span class="hljs-comment"># Setting the child KV with the secret value that is set in the parent KV</span>
            $Secret = ConvertTo-SecureString -<span class="hljs-keyword">String</span> $secretValue -AsPlainText -Force
            Set-AzKeyVaultSecret -VaultName $childKv -Name $secretName -SecretValue $Secret

            Write-Output <span class="hljs-string">"A new version of secret $(<span class="hljs-subst">$secretName</span>) has been created in KV - $(<span class="hljs-subst">$childKv</span>) with value - $(<span class="hljs-subst">$secretValue</span>)"</span>
        }        
    }
}
<span class="hljs-keyword">else</span>
{
    write-<span class="hljs-built_in">Error</span> <span class="hljs-string">"No input data found."</span> 
}
</code></pre><p>$WebhookData is the parameter where we will get the event data. I have converted the data into JSON for easier manipulation and ripped the KV's name, Secret name and event type.</p>
<pre><code>$bodyJson <span class="hljs-operator">=</span>  $WebhookData.RequestBody <span class="hljs-operator">|</span> ConvertFrom<span class="hljs-operator">-</span>Json
$secretName <span class="hljs-operator">=</span> $bodyJson[<span class="hljs-number">0</span>].data.ObjectName
$parentKv <span class="hljs-operator">=</span> $bodyJson[<span class="hljs-number">0</span>].data.VaultName    
$eventType <span class="hljs-operator">=</span> $bodyJson[<span class="hljs-number">0</span>].eventType
</code></pre><p>After checking the event type we needed, I am using the Az cmd to authenticate the current execution.</p>
<pre><code><span class="hljs-keyword">Connect</span>-AzAccount -<span class="hljs-keyword">Identity</span>
</code></pre><p>Getting the secret's latest version value from the KV</p>
<pre><code>$secretValue <span class="hljs-operator">=</span> Get<span class="hljs-operator">-</span>AzKeyVaultSecret <span class="hljs-operator">-</span>VaultName $parentKv <span class="hljs-operator">-</span>Name $secretName <span class="hljs-operator">-</span>AsPlainText
</code></pre><p>Setting the child KV with the secret value that is set in the parent KV</p>
<pre><code>$Secret <span class="hljs-operator">=</span> ConvertTo<span class="hljs-operator">-</span>SecureString <span class="hljs-operator">-</span>String $secretValue <span class="hljs-operator">-</span>AsPlainText <span class="hljs-operator">-</span>Force
Set<span class="hljs-operator">-</span>AzKeyVaultSecret <span class="hljs-operator">-</span>VaultName $childKv <span class="hljs-operator">-</span>Name $secretName <span class="hljs-operator">-</span>SecretValue $Secret
</code></pre><p>Publish this runbook and we shall test the setup in the next step. For this blog, I have hardcoded the secret names; you can change the logic as per your need.</p>
<h2 id="heading-step-6-test-the-setup">Step 6: Test the setup</h2>
<p>Now, create a new version in the Parent KV's secret. This will fire an event to the Webhook. You can monitor this in the KV's metric as like in the below pic,</p>
<p><img src="https://user-images.githubusercontent.com/5276778/144619881-3cc645b8-466d-4229-aed9-7899d976cf02.png" alt="image" /></p>
<p>You can also check the Webhook for the Last triggered value,</p>
<p><img src="https://user-images.githubusercontent.com/5276778/144620210-bd687145-51ed-47c3-a75a-f38285f864d4.png" alt="image" /></p>
<p>Output section in the Jobs section will give you the output of the PS code that was executed for the event. Here is the example output,</p>
<pre><code>[{<span class="hljs-string">"id"</span>:<span class="hljs-string">"6e1c172c-26f5-49e6-a065-f9ee3818b7e1"</span>,<span class="hljs-string">"topic"</span>:<span class="hljs-string">"Removing this for security"</span>,<span class="hljs-string">"subject"</span>:<span class="hljs-string">"secret-child-2"</span>,<span class="hljs-string">"eventType"</span>:<span class="hljs-string">"Microsoft.KeyVault.SecretNewVersionCreated"</span>,<span class="hljs-string">"data"</span>:{<span class="hljs-string">"Id"</span>:<span class="hljs-string">"Removing this for security"</span>,<span class="hljs-string">"VaultName"</span>:<span class="hljs-string">"parent-kv"</span>,<span class="hljs-string">"ObjectType"</span>:<span class="hljs-string">"Secret"</span>,<span class="hljs-string">"ObjectName"</span>:<span class="hljs-string">"secret-child-2"</span>,<span class="hljs-string">"Version"</span>:<span class="hljs-string">"Removing this for security"</span>,<span class="hljs-string">"NBF"</span>:null,<span class="hljs-string">"EXP"</span>:null},<span class="hljs-string">"dataVersion"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"metadataVersion"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"eventTime"</span>:<span class="hljs-string">"2021-12-03T12:18:25.6002045Z"</span>}]


Environments                                                                                                            
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>                                                                                                            
{[AzureChinaCloud, AzureChinaCloud], [AzureCloud, AzureCloud], [AzureGermanCloud, AzureGermanCloud], [AzureUSGovernme...

A <span class="hljs-keyword">new</span> version <span class="hljs-keyword">for</span> secret secret<span class="hljs-operator">-</span>child<span class="hljs-number">-2</span> has been created in KV <span class="hljs-operator">-</span> parent<span class="hljs-operator">-</span>kv with value <span class="hljs-operator">-</span> <span class="hljs-number">741258963</span>


SecretValue : System.Security.SecureString
Attributes  : Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultSecretAttributes
Enabled     : True
Expires     : 
NotBefore   : 
Created     : <span class="hljs-number">12</span><span class="hljs-operator">/</span><span class="hljs-number">3</span><span class="hljs-operator">/</span><span class="hljs-number">2021</span> <span class="hljs-number">12</span>:<span class="hljs-number">18</span>:<span class="hljs-number">30</span> PM
Updated     : <span class="hljs-number">12</span><span class="hljs-operator">/</span><span class="hljs-number">3</span><span class="hljs-operator">/</span><span class="hljs-number">2021</span> <span class="hljs-number">12</span>:<span class="hljs-number">18</span>:<span class="hljs-number">30</span> PM
ContentType : 
Tags        : 
TagsTable   : 
VaultName   : child<span class="hljs-number">-2</span><span class="hljs-operator">-</span>kv
Name        : secret<span class="hljs-operator">-</span>child<span class="hljs-number">-2</span>
Version     : Removing <span class="hljs-built_in">this</span> <span class="hljs-keyword">for</span> security
Id          : Removing <span class="hljs-built_in">this</span> <span class="hljs-keyword">for</span> security

A <span class="hljs-keyword">new</span> version of secret secret<span class="hljs-operator">-</span>child<span class="hljs-number">-2</span> has been created in KV <span class="hljs-operator">-</span> child<span class="hljs-number">-2</span><span class="hljs-operator">-</span>kv with value <span class="hljs-operator">-</span> <span class="hljs-number">741258963</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[How to build Azure Log Analytics URL with KQL Query?]]></title><description><![CDATA[This is a requirement I had for one of my projects where we retrieve app insights data from several azure web apps on their flow performances. I used App Insights API for posting the KQL query and the timespan to get the flow performances which are l...]]></description><link>https://blogs.aspnet.in/how-to-build-azure-log-analytics-url-with-kql-query</link><guid isPermaLink="true">https://blogs.aspnet.in/how-to-build-azure-log-analytics-url-with-kql-query</guid><category><![CDATA[Azure]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Sat, 27 Nov 2021 18:51:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638038899616/V2KMYuSk4E.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638038899616/V2KMYuSk4E.png" alt="image.png" /></p>
<p>This is a requirement I had for one of my projects where we retrieve app insights data from several azure web apps on their flow performances. I used App Insights API for posting the KQL query and the timespan to get the flow performances which are logged using custom dimensions in the specific insights' logs.</p>
<p>I also had a requirement where I had to give the users who are seeing the data, the ability to see the query and timespan I used for retrieving the data they are seeing. There is no direct way Azure gives you this ability. So, I researched the Log Analytics 'Share' functionality on how they are sharing the queries in the URLs.</p>
<p>Here is the format they are using,</p>
<pre><code><span class="hljs-attribute">https</span>://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%<span class="hljs-number">2</span>Fsubscriptions%<span class="hljs-number">2</span>F{subscriptionId}%<span class="hljs-number">2</span>FresourceGroups%<span class="hljs-number">2</span>F{rg}%<span class="hljs-number">2</span>Fproviders%<span class="hljs-number">2</span>Fmicrosoft.insights%<span class="hljs-number">2</span>Fcomponents%<span class="hljs-number">2</span>F{resourcename}/source/LogsBlade.AnalyticsShareLinkToQuery/query/{query}
</code></pre><p>The values you see inside {} are to be replaced with actuals. However, for {query}, it does not take the KQL directly.</p>
<p>I eventually found that they are converting the KQL into Base64 bytes and compressing them. They have this property by default set to true =&gt; isQueryBase64Compressed=true in the query string.</p>
<p>So, the URL would become like this,</p>
<pre><code><span class="hljs-attribute">https</span>://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%<span class="hljs-number">2</span>Fsubscriptions%<span class="hljs-number">2</span>F{subscriptionId}%<span class="hljs-number">2</span>FresourceGroups%<span class="hljs-number">2</span>F{rg}%<span class="hljs-number">2</span>Fproviders%<span class="hljs-number">2</span>Fmicrosoft.insights%<span class="hljs-number">2</span>Fcomponents%<span class="hljs-number">2</span>F{resourcename}/source/LogsBlade.AnalyticsShareLinkToQuery/query/{query}/isQueryBase<span class="hljs-number">64</span>Compressed/true
</code></pre><p>OK, Here is the code for that KQL query conversion to fit into this URL.</p>
<pre><code><span class="hljs-keyword">var</span> queryBytes = Encoding.UTF8.GetBytes(query);
<span class="hljs-keyword">var</span> memoryStream = <span class="hljs-keyword">new</span> MemoryStream();
<span class="hljs-keyword">var</span> gZipStream = <span class="hljs-keyword">new</span> GZipStream(memoryStream, CompressionMode.Compress);

gZipStream.Write(queryBytes, <span class="hljs-number">0</span>, queryBytes.Length);
gZipStream.Close();

<span class="hljs-keyword">var</span> compressedData = memoryStream.ToArray();

<span class="hljs-keyword">var</span> encodedText = Convert.ToBase64String(compressedData);
encodedText = encodedText.Replace(<span class="hljs-string">"/"</span>, <span class="hljs-string">"%2F"</span>);
encodedText = encodedText.Replace(<span class="hljs-string">"+"</span>, <span class="hljs-string">"%2B"</span>);
encodedText = encodedText.Replace(<span class="hljs-string">"="</span>, <span class="hljs-string">"%3D"</span>);

<span class="hljs-keyword">var</span> portalUrl = <span class="hljs-string">"https://portal.azure.com/#@sample.onmicrosoft.com/resource/subscriptions/guid-guid-guid-guid-guid/resourceGroups/rg-name/providers/microsoft.insights/components/app-insights-name/overview"</span>;

<span class="hljs-keyword">var</span> (subscriptionId, rg, resourcename) = GetAzureResourceDetailsFromLogUrl(portalUrl);

url = <span class="hljs-string">"https://portal.azure.com/#blade/"</span> + <span class="hljs-string">$"Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F<span class="hljs-subst">{subscriptionId}</span>%2FresourceGroups%2F<span class="hljs-subst">{rg}</span>%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F<span class="hljs-subst">{resourcename}</span>/source/LogsBlade.AnalyticsShareLinkToQuery/query/<span class="hljs-subst">{encodedText}</span>/isQueryBase64Compressed/true"</span>;
</code></pre><p><strong>Additional Perks</strong>
Here is an additional perk for getting the Subscription Id, Resource Group Name, and the Resource Name from an Azure App Insights resource URL.</p>
<pre><code>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> (<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>) GetAzureResourceDetailsFromLogUrl(<span class="hljs-built_in">string</span> portalUrl)
{
  <span class="hljs-keyword">var</span> uri = <span class="hljs-keyword">new</span> Uri(portalUrl.Replace(<span class="hljs-string">"#"</span>, <span class="hljs-string">""</span>));

  <span class="hljs-keyword">var</span> subscriptionId = uri.Segments[uri.Segments.ToList().FindIndex(<span class="hljs-function"><span class="hljs-params">f</span> =&gt;</span> f.Equals(SubscriptionsUrlSegment, StringComparison.OrdinalIgnoreCase)) + <span class="hljs-number">1</span>].Replace(<span class="hljs-string">"/"</span>, <span class="hljs-string">""</span>);

  <span class="hljs-keyword">var</span> rg = uri.Segments[uri.Segments.ToList().FindIndex(<span class="hljs-function"><span class="hljs-params">f</span> =&gt;</span> f.Equals(RGUrlSegment, StringComparison.OrdinalIgnoreCase)) + <span class="hljs-number">1</span>].Replace(<span class="hljs-string">"/"</span>, <span class="hljs-string">""</span>);

  <span class="hljs-keyword">var</span> resourceName = uri.Segments[uri.Segments.ToList().FindIndex(<span class="hljs-function"><span class="hljs-params">f</span> =&gt;</span> f.Equals(ComponentUrlSegment, StringComparison.OrdinalIgnoreCase)) + <span class="hljs-number">1</span>].Replace(<span class="hljs-string">"/"</span>, <span class="hljs-string">""</span>);

  <span class="hljs-keyword">return</span> (subscriptionId, rg, resourceName);
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Nuke Build - best build automation system]]></title><description><![CDATA[There were a number of build automation systems for the .net ecosystem. All had their own pros which helped many developers in the build teams. Developers in the other teams also had chances to work in the build automation systems. But some build sys...]]></description><link>https://blogs.aspnet.in/nuke-build-best-automation-system</link><guid isPermaLink="true">https://blogs.aspnet.in/nuke-build-best-automation-system</guid><category><![CDATA[Powershell]]></category><category><![CDATA[build]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Thu, 29 Oct 2020 19:24:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1604691283562/90R-cZrxJ.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There were a number of build automation systems for the .net ecosystem. All had their own pros which helped many developers in the build teams. Developers in the other teams also had chances to work in the build automation systems. But some build systems needed full-time build engineers to code and run those systems.</p>
<p>As a web developer, I too had a chance to work in the build automation systems,</p>
<ol>
<li><a target="_blank" href="https://www.finalbuilder.com/">FinalBuilder </a> - with a GUI, it was too easy to prepare and run the builds.</li>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/powershell?view=azure-devops&amp;tabs=yaml">PowerShell </a> - build system with PowerShell scripts was powerful yet complex to write.</li>
<li><a target="_blank" href="https://cakebuild.net/">Cake </a> (C# Make) - simple, easy to set up, and with a lot of extensions scripting with C#.</li>
</ol>
<p>BUT, now I had the opportunity to use  <a target="_blank" href="https://nuke.build/">Nuke.Build</a>  for my build system and I am obsessed heavily with it that I love it so much.</p>
<p>Comparing all these 4 build systems, from my point of view, I would recommend Nuke. We would not need a separate build team while using Nuke as it is coupled with your project itself. You would be able to debug Nuke targets in your own IDE.</p>
<p>Here is a sample nuke project for your reference.
https://github.com/vengi83644/nuke-build-sample</p>
<p>Similar blog: 
https://pknopf.com/post/2019-03-10-you-dont-need-cake-anymore-the-way-to-build-dotnet-projects-going-forward/</p>
]]></content:encoded></item><item><title><![CDATA[Bold BI on Linux and Mac Machines]]></title><description><![CDATA[Bold BI  is a powerful business intelligence dashboard software that helps you get meaningful insights from your business data and make better decisions.
Bold BI is available both as a SaaS(Bold BI Cloud) and on-premise(Bold BI Embedded). 
Bold BI Cl...]]></description><link>https://blogs.aspnet.in/bold-bi-on-linux-and-mac-machines</link><guid isPermaLink="true">https://blogs.aspnet.in/bold-bi-on-linux-and-mac-machines</guid><category><![CDATA[Linux]]></category><category><![CDATA[mac]]></category><category><![CDATA[.net core]]></category><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Wed, 23 Sep 2020 19:05:34 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://www.boldbi.com/">Bold BI</a>  is a powerful business intelligence dashboard software that helps you get meaningful insights from your business data and make better decisions.</p>
<p>Bold BI is available both as a SaaS(Bold BI Cloud) and on-premise(Bold BI Embedded). </p>
<h1 id="bold-bi-cloud">Bold BI Cloud</h1>
<p>We are serving business intelligence Software as a Service using Azure cloud infrastructure. You can just sign up for an account and start dashboarding in seconds.</p>
<p>Sign Up Free for a cloud business intelligence account  <a target="_blank" href="https://www.boldbi.com/cloud">here</a>.</p>
<h1 id="bold-bi-embedded">Bold BI Embedded</h1>
<p>We are also giving you the software to be deployed in your environment if you would like your data to be kept with you(by the way, we also support hosting our cloud solution in any region as needed).</p>
<p>As of now, we only support windows machines for the on-premise version. But here is great news. We are now porting our product to support Linux and Mac machines too.</p>
<p>We are targeting Nov 2020 for the release of Bold BI to deployed in any environment. Of course, dockers too.</p>
<p>Stay tuned for the updates.,.,.,.,</p>
]]></content:encoded></item><item><title><![CDATA[Top 5 features released in Bold BI v3.3.40]]></title><description><![CDATA[I wanted my first blog to be about the product that I am a part of. This is Bold BI from Syncfusion.
For those of you who don't know what is Bold BI, here is a short intro.
Bold BI is a business intelligence software available both as a SaaS and an E...]]></description><link>https://blogs.aspnet.in/top-5-features-released-in-bold-bi-v3340</link><guid isPermaLink="true">https://blogs.aspnet.in/top-5-features-released-in-bold-bi-v3340</guid><dc:creator><![CDATA[Venkatesan Rethinam]]></dc:creator><pubDate>Wed, 19 Aug 2020 13:32:03 GMT</pubDate><content:encoded><![CDATA[<p>I wanted my first blog to be about the product that I am a part of. This is Bold BI from Syncfusion.</p>
<p>For those of you who don't know what is Bold BI, here is a short intro.</p>
<p>Bold BI is a business intelligence software available both as a SaaS and an Enterprise version. It is a complete revamp of the Syncfusion's Dashboard Platform which was launched in 2015.</p>
<p>November 2015 =&gt;  <a target="_blank" href="https://www.boldbi.com/news/press-releases/syncfusion-announces-dashboard-platform">Syncfusion Announces Dashboard Platform</a> </p>
<p>June 2019 =&gt; <a target="_blank" href="https://www.boldbi.com/news/press-releases/syncfusion-launches-new-business-intelligence-platform">Bold BI was born - SaaS version</a></p>
<p>October 2019 =&gt; <a target="_blank" href="https://www.boldbi.com/news/press-releases/syncfusion-releases-on-premise-and-embedded-versions-of-bold-bi">Bold BI was released for on-premise</a></p>
<p><strong>Quicklinks:</strong>
<a target="_blank" href="https://www.boldbi.com">Sign Up</a>, <a target="_blank" href="https://help.syncfusion.com/bold-bi">Documentation</a>, <a target="_blank" href="https://www.boldbi.com/blog">Blogs</a>, <a target="_blank" href="https://www.boldbi.com/solutions/overview">Solutions</a>, <a target="_blank" href="https://www.boldbi.com/release-history">Release history</a></p>
<p>Coming again to the blog, I am going to cover the top 5 features I liked the most in Bold BI in the latest release v3.3.40.</p>
<ol>
<li><p><strong>Combine multiple visuals</strong> into one block - Group Visuals
Grouping visuals lets you use the group as a single widget thereby moving, resizing easier, and visualize the data faster.
<img src="//cdn.boldbi.com/wp/release-history/v3.3.40/combined_widget.gif" alt="Combine Visuals" /></p>
</li>
<li><p><strong>Combine data sources</strong> - the most wanted feature by the Bold BI customers
Say, if you want to compare the goal conversions in Google Analytics with the actual leads created in Salesforce, you can connect both of them in Bold BI and combine them as a single data source to visualize in the dashboards.
<img src="//cdn.boldbi.com/wp/release-history/v3.3.40/combined_data_source.gif" alt="Combine Data Sources" /></p>
</li>
<li><p><strong>Publish dashboards and data sources to other sites in Bold BI</strong> - 
This is a helpful feature that comes in handy when you want to move dashboards/data sources created in one Bold BI Server to another. Below are the use cases it covers,
i. When you have dev, staging, and production servers
ii. When you have multiple customers having their own tenant in the Bold BI multi-tenant application(Oh, Yes! <a target="_blank" href="https://help.syncfusion.com/bold-bi/on-premise/multi-tenancy/overview">Bold BI is a multi-tenant application server</a>) and want to publish the same dashboard (but with different data sources - this is covered by the next feature).
<img src="//cdn.boldbi.com/wp/release-history/v3.3.40/Publish_Module.gif" alt="Publish Module" /></p>
</li>
<li><p><strong>Data Security</strong> - new additions
i. Isolation Code
ii. Custom Attributes
These are new additions to the Data Security features in Bold BI. These are simply parameters that filter the data based on the user, group, or the tenant itself, yet powerful in their own forms.
<img src="//cdn.boldbi.com/wp/release-history/v3.3.40/Custom_Attribute.gif" alt="Isolation Code" />
<img src="//cdn.boldbi.com/wp/release-history/v3.3.40/Isolation_Code.gif" alt="Custom Attributes" /></p>
</li>
<li><p><strong>New Data Sources</strong> - as a custom, new data sources are added in every release until there are no other data sources in the market
Bold BI now supports 120+ data sources. Newly added are the below,</p>
</li>
</ol>
<p>EDI, SQLite, Influx DB, A separate connector for CData, LinkedIn, Zoho, Zendesk CRM, Commvault, Xero, Lessonly, SAP HANA data source through ODBC</p>
<p>See for yourself from the docs,</p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/visualize-data/configure-widgets/combine-widget">Combine Widgets</a></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/combine-data-sources">Combine Data Sources</a></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/enterprise-bi/manage-dashboards/publish-dashboards">Publish Module</a></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/configuring-isolation-code">Isolation Code</a></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/configuring-custom-attribute">Custom Attributes</a></p>
<p><strong>Other useful links,</strong></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/embedded-bi/javascript/overview">Embedded BI - JS based</a></p>
<p><a target="_blank" href="https://embed-sdk.boldbi.com">Embedded BI Demo</a></p>
<p><a target="_blank" href="https://adhoc.boldbi.com/embed">Adhoc demo</a></p>
<p><a target="_blank" href="https://help.syncfusion.com/bold-bi/enterprise-bi/rebranding/overview">Custom Branding</a></p>
]]></content:encoded></item></channel></rss>