<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Boris Zaikin</title>
    <description>The latest articles on DEV Community by Boris Zaikin (@boriszn).</description>
    <link>https://dev.to/boriszn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F68727%2Fab02bf09-711f-4d64-b847-6c423f06425c.jpg</url>
      <title>DEV Community: Boris Zaikin</title>
      <link>https://dev.to/boriszn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/boriszn"/>
    <language>en</language>
    <item>
      <title>Internal Developer Platform In Plain English</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Wed, 08 Jun 2022 13:59:54 +0000</pubDate>
      <link>https://dev.to/boriszn/internal-developer-platform-in-plain-english-5b2l</link>
      <guid>https://dev.to/boriszn/internal-developer-platform-in-plain-english-5b2l</guid>
      <description>&lt;h3&gt;
  
  
  Pros and Cons of Using Internal Development Platform in the Cloud Computing World
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Internal Development Platform (IDP) as a brand new word in the DevOps and product development. In this article, I'm explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What exactly IDP is&lt;/li&gt;
&lt;li&gt;How to involve it in your organization&lt;/li&gt;
&lt;li&gt;An overview of the existing IDP platforms on the market, their pros, and cons&lt;/li&gt;
&lt;li&gt;An example of IDP Architecture based on the Azure Cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is an internal developer platform?
&lt;/h2&gt;

&lt;p&gt;As a software architect, I design the DevOps processes and spend much time selecting the DevOps toolset, building pipelines, and communicating with the DevOps team to implement some procedures for the development team. We can solve this with the internal development platform (IDP).&lt;/p&gt;

&lt;p&gt;An internal developer platform(IDP) is an additional layer between development and DevOps teams. The main idea behind the IDP is to remove the dependency between DevOps and developers.&lt;/p&gt;

&lt;p&gt;Let's look at what functions and components you should include in the IDP. The IDP platform should provide the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;RBAC management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Infrastructure management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuration management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment management&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's have a look at what is the IDP from insights and how it is connected with DevOps tools and cloud providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  IDP and DevOps
&lt;/h2&gt;

&lt;p&gt;As seen in the previous example, IDPs are like frameworks built on top of DevOps tools, resources, and cloud providers. Some developer platforms may include an option to work with numerous DevOps tools and cloud providers, like in the diagram above. Other IDPs focused on specific ones.&lt;/p&gt;

&lt;p&gt;The diagram below shows the example of the IDP components and how they combine DevOps tools and cloud providers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xcafHE4f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/12738/1%2AfMWlCcd5ZZfSB3GGukREuQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xcafHE4f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/12738/1%2AfMWlCcd5ZZfSB3GGukREuQ.jpeg" alt="" width="880" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's see what options of developer platforms are on the market. For example, &lt;a href="https://github.com/gimlet-io"&gt;Gimplet.IO&lt;/a&gt; is an open-source IDP that focuses primarily on operating Kubernetes deployments. Gimlet contains CLI to manage charts, deployments, secrets, and GitOps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--51v45A7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ABSGIXmS1Zp8XnVUj" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--51v45A7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ABSGIXmS1Zp8XnVUj" alt="" width="729" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Gimlet doesn't contain application configuration management which makes it partial IDP. I like the idea when IDP focuses on specific platforms. For example, Kubernetes. It makes it more stable than other platforms that try to cover all well-known platforms and cloud providers. Covering too many platforms can be suitable for marketing. However, having the proper support of multiple platforms and cloud providers requires significant effort as they have different APIs, standards, and even naming conventions.&lt;/p&gt;

&lt;p&gt;Another good example is an &lt;a href="https://www.upbound.io"&gt;Upbound&lt;/a&gt;. Upbound is a powerful IDP that allows you to deploy and build your application in the Kubernetes cluster. It supports GCP, AWS and the Goolge Cloud. I like how an Upbound team supports the documentation and provides architecture examples and architecture references. Below you can see &lt;a href="https://github.com/upbound/platform-ref-azure"&gt;Azure Reference Architecture for Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--31Up9pio--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AHXJvy4vx8CNsPkOS" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--31Up9pio--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AHXJvy4vx8CNsPkOS" alt="" width="880" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are products like &lt;a href="https://github.com/backstage/backstage"&gt;Backstage&lt;/a&gt; that made good progress in supporting different platforms. Backstage is an open-source platform made by Spotify and supported by &lt;a href="https://www.cncf.io"&gt;Cloud Native Computing Foundation (CNCF)&lt;/a&gt;. The Backstage is a platform that is intended to build developer portals. It includes the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://backstage.io/docs/features/software-catalog/software-catalog-overview"&gt;Backstage Software Catalog&lt;/a&gt; for managing microservices, libraries, data pipelines, websites, and ML models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://backstage.io/docs/features/software-templates/software-templates-index"&gt;Backstage Software Templates&lt;/a&gt; for quickly creating new projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://backstage.io/docs/features/techdocs/techdocs-overview"&gt;Backstage TechDocs&lt;/a&gt; for generating project documentation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://www.devopsbox.io/profit-calculator"&gt;DevOpsBox&lt;/a&gt; is an example of a platform that supports numerous DevOps platforms, and Cloud Providers offer application configuration management, infrastructure orchestration, environment management, and adequate RBAC. However, many cloud providers and tools are still in the implementation phase ' for example, &lt;a href="https://www.devopsbox.io/profit-calculator"&gt;Azure and Google Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d5p3PpwG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2AMuGxPcFS5S5OVcvl" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d5p3PpwG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2AMuGxPcFS5S5OVcvl" alt="" width="880" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And last but not least is a &lt;a href="https://github.com/Humanitec/about"&gt;Humanitec&lt;/a&gt;. It is a platform that connects multiple GitOps tools and Cloud platforms. I like the concept that Humanitec brings. For example, you can &lt;a href="https://docs.humanitec.com/introduction/how-does-humanitec-integrate"&gt;connect&lt;/a&gt; one or several Kubernetes clusters over Azure and AWS. Connect Datadog and Grafana. You can even reuse your existing Terraform and Pulumi scripts. You can operate all this infrastructure via CLI, API, and developer-focused UI. Developers can quickly provision Kubernetes clusters, spinup databases, and VM instances without writing pipelines, manifests, and Terraform scripts.&lt;/p&gt;

&lt;p&gt;They don't have to wait for the DevOps team when they provide this one. However, DevOps specialists still should spend time on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Selecting DevOps tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrating existing scripts, pipelines, and manifests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect an environment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZPYJHKKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ASlF8c8cHwqE4MMTX" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZPYJHKKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ASlF8c8cHwqE4MMTX" alt="" width="880" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Humanitec also supports the &lt;a href="https://platformcon.com/"&gt;PlatformCON&lt;/a&gt; community around IDP and DevOps topics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example of internal development platform
&lt;/h2&gt;

&lt;p&gt;Many enterprise companies like Spotify are building their own IDP. They already have a massive DevOps code base that includes scripts and templates, pipelines for the most available platforms, and cloud providers.&lt;/p&gt;

&lt;p&gt;A few years ago, I was working for an enterprise company with 500+ ongoing projects, and they needed to set up a team for the new project quickly. All projects need to provide infrastructure ASAP. The developer had no time to do DevOps stuff. The company asked to build Internal Developer Platform (IDP) to provision and manage infrastructure. The idea is simple. When a new project appears, the algorithm should be the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Onboard a development team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The support engineers provision all required infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BuVa_ZX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AoczaJY_34Bib_uug" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BuVa_ZX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AoczaJY_34Bib_uug" alt="" width="880" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the architecture is primarily based on the Azure stack. However, you can easily extend it to use in AWS, GoogleCloud, or any other cloud provider. The IDP can provide the following resources and applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Azure DevTestLabs, VMs, and Extensions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kubernetes Clusters (AKS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Databases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;App Service&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A developer portal is a simple ReactJS application that uses an internal API. The API is based on Azure API management and contains several functions representing a single API call. For example, to trigger deployment or receive deployment status. The API layer is connected to the Azure DevOps API. The Azure DevOps contains YAML pipelines, Terraform, Bash, and PowerShell scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;p&gt;An IDP provides you with the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Developer's productivity. Developers can deliver the code and focus less on DevOps tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DevOps teams freedom. An IDP reduces the infrastructure provisioning workload and focuses on building baseline configuration and templates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IDP can bring value to the product. However, there are some drawbacks involving IDP. Below I've listed some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Costs and complexity. Building a developer platform requires companies to provide additional resources to build, support, and keep it updated. We can partially solve this issue by buying the external development platform. I will describe a few existing platforms below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Culture change. Involving a developer platform requires the development and DevOps team to change their culture and way of working. It may end up with the teams will lose time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list can be endless and may require its own article. In the article: &lt;a href="https://www.bunnyshell.com/blog/companies-build-internal-dev-platforms-pitfalls"&gt;Why companies that build internal dev platforms wouldn't do it again&lt;/a&gt; you can find a lot more points against building IDP internally.&lt;/p&gt;

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

&lt;p&gt;Here I've tried to clarify what is an internal developer platform and how you and your organization can benefit from involving IDP. Also, I've added points about why using IDP may be not reasonable. Also, I've shared the architecture example that demonstrates how an organization can build its IDP and save time on managing several DevOps teams across the company.&lt;/p&gt;

</description>
      <category>idp</category>
      <category>cloud</category>
      <category>azure</category>
      <category>aws</category>
    </item>
    <item>
      <title>CI/CD for Cloud-Native Applications"</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Wed, 20 Apr 2022 14:35:08 +0000</pubDate>
      <link>https://dev.to/boriszn/cicd-for-cloud-native-applications-48d8</link>
      <guid>https://dev.to/boriszn/cicd-for-cloud-native-applications-48d8</guid>
      <description>&lt;h2&gt;
  
  
  Define continuous integration and continuous delivery, review the steps in a CI/CD pipeline, and explore DevOps and IaC tools used to build a CI/CD process.
&lt;/h2&gt;

&lt;p&gt;Continuous integration (CI) and continuous delivery (CD) are crucial parts of developing and maintaining any cloud-native application. From my experience, proper adoption of tools and processes makes a CI/CD pipeline simple, secure, and extendable. Cloud native (or cloud based) simply means that an application utilizes cloud services. For example, a cloud-native app can be a web application deployed via Docker containers and uses Azure Container Registry deployed to Azure Kubernetes Services or uses Amazon EC2, AWS Lambda, or Amazon S3 services.&lt;/p&gt;

&lt;p&gt;In this article, we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Define continuous integration and continuous delivery&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review the steps in a CI/CD pipeline&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Explore DevOps and IaC tools used to build a CI/CD process&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  An Overview of CI/CD
&lt;/h2&gt;

&lt;p&gt;The continuous integration process is when software engineers combine all parts of the code to validate before releasing tested applications to dev, test, or production stages. CI includes the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Source control&lt;/strong&gt; - Pulls the latest source code of the application from source control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build&lt;/strong&gt; - Compiles, builds, and validates the code or creates bundles and linting in terms of JavaScript/Python code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt; - Runs unit tests and validates coding styles.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Following CI is the continuous delivery process, which includes the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt; - Places prepared code into the test (or stage) environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt; - Runs integration and/or load tests. This step is optional as an application can be small and not have a huge load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release&lt;/strong&gt; - Deploys an application to the development, test, and production stages.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my opinion, CI and CD are two parts of one process. However, in the cloud-native world, you can implement CD without CI. You can see the whole CI/CD process in the diagram below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8hM1QBza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ACDpQIoJqZMu6SWi1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8hM1QBza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2ACDpQIoJqZMu6SWi1.jpg" alt="" width="880" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Importance of CI/CD in Cloud-Native Application Development
&lt;/h2&gt;

&lt;p&gt;Building a successful cloud-native CI/CD process relies on the Infrastructure-as-Code (IaC) toolset. Many cloud-native applications have integrated CI/CD processes that include steps to build and deploy the app and provision and manage its cloud resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  How IAC Supports CI/CD
&lt;/h2&gt;

&lt;p&gt;Infrastructure as Code is an approach where you can describe and manage the configuration of your application's infrastructure. Many DevOps platforms support the IaC approach, integrating it directly into the venue for example, Azure DevOps, GitHub, GitLab, and Bitbucket support YAML pipelines. With the YAML pipeline, you can build CI/CD processes for your application with infrastructure deployment. Below, you can see DevOps platforms that can easily be integrated with IaC tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Azure Resource Manager, Bicep, and Farmer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Terraform&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tekton&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pulumi&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS CloudFormation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building a Successful Cloud-Native CI/CD Process
&lt;/h2&gt;

&lt;p&gt;In many cases, the CI/CD process for cloud-native applications includes documenting and deploying infrastructure using an IaC approach. The IaC approach allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prepare infrastructure for your application before it is deployed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add new cloud resources and configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manage existing configurations and solve problems like "environment drift".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Environment drift problems appear when teams have to support multiple environments manually. Drift can lead to an inconsistent environment setting that causes application outages. A successful CI/CD process with IaC relies on what tool and platform you use.&lt;/p&gt;

&lt;p&gt;Let's have a look into the combination of the most popular DevOps and IaC tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure DevOps
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/?view=azure-pipelines"&gt;Azure DevOps&lt;/a&gt; is a widely used tool to organize and build your cloud-native CI/CD process, especially for Azure Cloud. It supports UI and YAML-based approaches to building pipelines. I prefer using this tool when building an automated CI/CD process for Azure Cloud. Let's look at a simple YAML pipeline that creates a virtual machine (VM) in &lt;a href="https://docs.microsoft.com/en-us/azure/devtest-labs/devtest-lab-overview"&gt;Azure DevTest Labs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    . . . . . .
    jobs:
    - deployment: deploy
      displayName: Deploy
      pool:
        vmImage: 'ubuntu-latest'
      environment: ${{ parameters.environment }}

      strategy:
        runOnce:
          deploy:
            steps:
            - checkout: self
            - task: AzurePowerShell@4
              displayName: 'Check vm-name variable exists'
              continueOnError: true
              inputs:
                azureSubscription: ${{ parameters.azSubscription }}
                scriptType: "inlineScript"
                azurePowerShellVersion: LatestVersion
                inline: |
                  $vm_name="$(vm-name)"
                  echo $(vm-name) - $vm_name
                  #if ([string]::IsNullOrWhitespace($vm_name))
                  {
                      throw "vm-name is not set"
                  }

            - task: AzureResourceManagerTemplateDeployment@3
              displayName: 'New deploy VM to DevTestLab'
              inputs:
                deploymentScope: 'Resource Group'
                azureResourceManagerConnection: ${{ parameters.azSubscription }}
                subscriptionId: ${{ parameters.idSubscription }}
                deploymentMode: 'Incremental'
                resourceGroupName: ${{ parameters.resourceGroup }}
                location: '$(location)'
                templateLocation: 'Linked artifact'
                csmFile: templates/vm.json
                csmParametersFile: templates/vm.parameters.json
                overrideParameters: '-labName "${{parameters.devTestLabsName}}"
                                     -vmName ${{parameters.vmName}} -password ${{parameters.password}}
                                     -userName ${{parameters.user}}
                                     -storageType
    . . . . . .

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

&lt;/div&gt;



&lt;p&gt;To simplify the pipeline listing, I've shortened the example above. As you can see, the pipeline code can also be generic; therefore, you can reuse it in multiple projects. The pipeline's first two steps are in-line PowerShell scripts that validate and print required variables. Then this pipeline can be integrated easily into Azure DevOps, as shown in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YlEcDoWn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2120/0%2A3fK6KjGtaY39V_ba.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YlEcDoWn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2120/0%2A3fK6KjGtaY39V_ba.jpg" alt="" width="880" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The last step creates the VM in Azure DevTest Labs using &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/overview"&gt;Azure Resource Manager&lt;/a&gt; (ARM) templates and is represented via the &lt;a href="https://docs.microsoft.com/en-us/azure/templates/microsoft.devtestlab/2015-05-21-preview/labs/virtualmachines?tabs=json"&gt;JSON format&lt;/a&gt;. This step is quite simple: It sends (overrides) variables into the ARM scripts, which Azure uses to create a VM in the DevTest Labs.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS CloudFormation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html"&gt;AWS CloudFormation&lt;/a&gt; is an IaC tool from the AWS Cloud stack and is intended to provision resources like EC2, DNS, S3 buckets, and many others. CloudFormation templates are represented in JSON and YAML formats; therefore, they can be an excellent choice to build reliable, cloud-native CI/CD processes. Also, many tools like Azure DevOps, GitHub, Bitbucket, and &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html"&gt;AWS CodePipelines&lt;/a&gt; have integration options for CloudFormation.&lt;/p&gt;

&lt;p&gt;Below is an example of what an AWS CloudFormation template may look like, which I've shortened to fit in this article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {
      "AWSTemplateFormatVersion" : "2010-09-09",
      "Parameters" : {
         "AccessControl" : {
         "Description" : " The IP address range that can be used to access the CloudFormer tool. NOTE:
    We highly recommend that you specify a customized address range to lock down the tool.",
          "Type": "String",
          "MinLength": "9",
          }
      },
          "Mappings" : {
            "RegionMap" : {
            "us-east-1" : { "AMI" : "ami-21341f48" },
            }
      }
      "Resources" : {
         "CFNRole": {
         "Type": "AWS::IAM::Role",
         "Properties": {
         "AssumeRolePolicyDocument": {
                "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": [ "ec2.amazonaws.com" ] },
                "Action": [ "sts:AssumeRole" ]
                }]
          },
          "Path": "/"
          }
          },
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html#:~:text=set%20of%20outputs-,Template%20sections,-Templates%20include%20several"&gt;CloudFormation templates&lt;/a&gt; contain sections including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Parameters - You can specify input parameters to run templates from the CLI or pass data from AWS CodePipeline (or any other CI/CD tool).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mappings - You can match the key to a specific value (or set of values) based on a region.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resources - You can declare the resources included, answering the "what will be provisioned" question, and you can adjust the resource according to your requirement using a parameter.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS CloudFormation templates look similar to Azure ARM templates as they do the same work but for different cloud providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Cloud Deployment Manager
&lt;/h2&gt;

&lt;p&gt;Google Cloud (GC) offers &lt;a href="https://cloud.google.com/deployment-manager/docs"&gt;Cloud Deployment Manager&lt;/a&gt;, the all-in-one tool that includes templates that describe resources for provisioning and templates to build CI/CD pipelines. The templates support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.python.org/3/"&gt;Python 3.x&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jinja.palletsprojects.com/en/2.10.x/"&gt;Jinja 2.10.x&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;YAML&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's explore a deployment of the VM using Jinja templates ' it looks common to YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    resources:
    - type: compute.v1.instance
      name: {{env["project"]}}-deployment-vm
      properties:
         zone: {{properties["zone"]}}
         machineType:https://www.googleapis.com/compute/v1/projects/{{env["project"]}}/zones/
    {{properties["zone"]}}/machineTypes/f1-micro
         disks:
         - deviceName: boot
         type: PERSISTENT
         boot: true
         autoDelete: true
         initializeParams:
         sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/
    debian-9
         networkInterfaces:
         - network: https://www.googleapis.com/compute/v1/projects/{{env["project"]}}/global/networks/
    default
         accessConfigs:
         - name: External NAT
         type: ONE_TO_ONE_NAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above is similar to ARM and CloudFormation templates as it describes resources to deploy. In my opinion, the YAML/Jinja 2.10.x format works better than the JSON-based structure because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;YAML increases readily and can read much faster than JSON&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams can find and fix errors in YAML and Jinja faster than in JSON&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;YAML pipelines (with small adaptations) can be reused on many other platforms&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find an extended version of this example in the &lt;a href="https://gist.github.com/ryu1kn/3f53143e743e2553030f3d7e93b0684e"&gt;GitHub gist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tekton and Kubernetes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tekton.dev/"&gt;Tekton&lt;/a&gt;, supported by the &lt;a href="https://cd.foundation/"&gt;CD Foundation&lt;/a&gt; (part of the &lt;a href="https://www.linuxfoundation.org/projects/"&gt;Linux Foundation&lt;/a&gt;), is positioned as an open-source CI/CD framework for cloud-native applications based on Kubernetes. The Tekton framework's components include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tekton Pipelines&lt;/strong&gt; - Most essential and are intended to build CI/CD pipelines based on &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/"&gt;Kubernetes Custom Resources&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tekton Triggers&lt;/strong&gt; - Provide logic to run pipelines based on an event-driven approach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tekton CLI&lt;/strong&gt; - Built on top of the Kubernetes CLI and allows you to run pipelines, check statuses, and manage other options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/tektoncd/dashboard/blob/main/README.md"&gt;Tekton Dashboard&lt;/a&gt; and &lt;a href="https://hub.tekton.dev/"&gt;Hub&lt;/a&gt; Use web-based graphical interfaces to run pipelines and observe pipeline execution logs, resource details, and resources for the entire cluster (see dashboard in Figure 3).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f78Nd1SS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2284/0%2AOQbKAPeD0-fpVqV3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f78Nd1SS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2284/0%2AOQbKAPeD0-fpVqV3.jpg" alt="" width="880" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I like the idea behind Tekton Hub that you can share your pipelines and other reusable components. Let's look at a Tekton Pipeline example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    apiVersion: tekton.dev/v1beta1
    kind: Pipeline
    metadata:
      name: say-things
    spec:
      tasks:
        - name: first-task
          params:
            - name: pause-duration
              value: "2"
            - name: say-what
              value: "Hello, this is the first task"
          taskRef:
            name: say-something
        - name: second-task
          params:
            - name: say-what
              value: "And this is the second task"
          taskRef:
            name: say-something
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipeline code above is written in the native Kubernetes format/manifests and represents a set of tasks. Therefore, you can build native cross-cloud CI/CD processes. In the tasks, you can add steps to operate with Kubernetes resources, build images, print information, and many other actions. You can find the complete tutorial for Tekton Pipelines &lt;a href="https://developers.redhat.com/blog/2021/01/13/getting-started-with-tekton-and-pipelines#run_in_parallel_or_sequentially"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform, Azure Bicep, and Farmer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/docs"&gt;Terraform&lt;/a&gt; is a leading platform for building reliable CI/CD processes based on the IaC approach. I will not go in depth on Terraform as it requires a separate article (or even book). Terraform uses a specific language that simplifies building CI/CD templates. Also, it allows you to reuse code parts, adding dynamic flavor to the CI/CD process. Therefore, you can build templates that are much better than ARM/JSON templates. Let's see a basic example of Terraform template code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    terraform {
      required_providers {
      }
      backend "remote" {
        organization = "YOUR_ORGANIZATION_NAME"
        workspaces {
          name = "YOUR_WORKSPACE_NAME"
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The basic template above contains the resources, providers, and workspace definition. The same approach follows the Azure Bicep and Farmer tools. These Terraform analogs can drastically improve and shorten your code. Let's look at the Bicep example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    param location string = resourceGroup().location
    param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}'

    resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
      name: storageAccountName
      location: location
      sku: {
        name: 'Standard_LRS'
      }
      kind: 'StorageV2'
      properties: {
        accessTier: 'Hot'
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Bicep code above deploys storage accounts in the US region. You can see that the &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=json"&gt;JSON ARM example&lt;/a&gt; acts the same. In my opinion, Terraform and Azure Bicep are shorter and much easier to understand than ARM templates. The &lt;a href="https://compositionalit.github.io/farmer/"&gt;Farmer&lt;/a&gt; tool can also show impressive results in readability and type-safe code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Create a storage account with a container
    let myStorageAccount = storageAccount {
        name "myTestStorage"
        add_public_container "myContainer"
    }

    // Create a web app with application insights that's connected to the storage account.
    let myWebApp = webApp {
        name "myTestWebApp"
        setting "storageKey" myStorageAccount.Key
    }

    // Create an ARM template
    let deployment = arm {
        location Location.NorthEurope
        add_resources [
            myStorageAccount
            myWebApp
        ]
    }

    // Deploy it to Azure!
    deployment
    |&amp;gt; Writer.quickDeploy "myResourceGroup" Deploy.NoParameters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, you can see how to deploy your web application to Azure in 20 lines of easily readable and extensible code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulumi
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.pulumi.com/registry/packages/azure-native/how-to-guides/azure-cs-net5-aks-webapp/"&gt;Pulumi&lt;/a&gt; framework follows a different approach to building and organizing your CI/CD processes: It allows you to deploy your app and infrastructure using your favorite programming language. Pulumi supports Python, C#, TypeScript, Go, and many others. Let's look at the example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public MyStack()
    {
        var app = new WebApplication("hello", new()
        {
            DockerImage = "strm/helloworld-http"
        });

        this.Endpoint = app.Endpoint;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part of the code deploys the web app to the Azure cloud and uses Docker containers to spin up the web app. You can find examples of how to spin up the web app in an AKS cluster as well as from the Docker container &lt;a href="https://www.pulumi.com/registry/packages/azure-native/how-to-guides/azure-cs-net5-aks-webapp/"&gt;here&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Building a cloud-native CI/CD pipeline for your application can be a never-ending story if you don't know the principles, tools, and frameworks best suited for doing so. It is easy to get lost in various tools, providers, and buzzwords, so this article aimed to explain what CI/CD cloud-native applications are and walk you through the widely used tools and principles of building reliable CI/CD pipelines. Having this guide helps you to feel comfortable while designing CI/CD processes and choosing the right tools for your cloud-native application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is an article from DZone's 2022 DevOps Trend Report.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;For more:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://dzone.com/trendreports/devops-3"&gt;Read the Report&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>cicd</category>
      <category>azure</category>
      <category>aws</category>
      <category>iac</category>
    </item>
    <item>
      <title>Common Performance Management Mistakes</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Tue, 21 Dec 2021 16:20:10 +0000</pubDate>
      <link>https://dev.to/boriszn/common-performance-management-mistakes-imi</link>
      <guid>https://dev.to/boriszn/common-performance-management-mistakes-imi</guid>
      <description>&lt;h2&gt;
  
  
  How to Avoid Pain Points Introduced by Cloud-Based Architectures
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Editor's Note: The following is an article written for and published in DZone's 2021 &lt;a href="https://dzone.com/trendreports/application-performance-management-1"&gt;Application Performance Management Trend Report&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Performance in any cloud-distributed application is key to successful user experience. Thus, having a deep understanding of how to measure performance and what metric IO pattern to use is quite important. In this article, we will cover the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Performance analysis checklist and strategies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance issue detection and tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance anti-patterns and how to mitigate issues&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Monitoring in the Cloud Checklist
&lt;/h2&gt;

&lt;p&gt;When improving the performance of your application or infrastructure architecture, use the following checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Introduce logging tools in your application and infrastructure components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Find anti-patterns and bottlenecks of your code and infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up monitoring tools to gather CPU, memory, and storage utilization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adjust monitoring tools to gather info about events between base components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do not log everything identify important events to log&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Most Common Application Performance Anti-Patterns
&lt;/h2&gt;

&lt;p&gt;The most important item from the performance checklist is identifying anti-patterns. This will save valuable time once you already know the weak spots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nosy Neighbor
&lt;/h2&gt;

&lt;p&gt;Imagine you have a microservice that is deployed as a Docker container, and it is eating more CPU and memory than other containers. That can lead to outages since other services might not receive enough resources. For example, if you use Kubernetes, it may kill other containers to release some resources. You can easily fix this by setting up CPU and memory limits at the very beginning of the design and implementation phases.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Caching
&lt;/h2&gt;

&lt;p&gt;Some applications that tend to work under a high load do not contain any caching mechanisms. This may lead to fetching the same data and overusing the main database. You can fix this by introducing a caching &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/best-practices/caching"&gt;layer in your application&lt;/a&gt;, and it can be based on a Redis cache or just a memory cache module. Of course, you don't need to use caching everywhere, since that may lead to data inconsistency. Sometimes, you can improve your performance by simply adding output caching to your code. For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace MvcApplication1.Controllers
{
  [HandleError]
  public class HomeController : Controller
  {

     [OutputCache(Duration=10, VaryByParam="none")]
     public ActionResult Index()
     {
         return View();
     }
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Above, I've added the output cache attribute to the MVC application. It will cache static content for 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Busy Database
&lt;/h2&gt;

&lt;p&gt;This issue is often found in modern microservices architectures, when all services are in a Kubernetes cluster and deployed via containers but they all use a single database instance. You can fix this problem by identifying the data scope for each microservice and splitting one database into several. You can also use the database pools mechanism. For example, Azure provides the Azure SQL elastic pool service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry Storm
&lt;/h2&gt;

&lt;p&gt;Retrying storms and the issues they cause usually occur in microservice or cloud-distributed applications; when some component or service is offline, other services try to reach it. This often results in a never-ending retry loop. It can be fixed, however, by using the &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker"&gt;circuit breaker&lt;/a&gt; pattern. The idea for circuit breaker comes from radio electronics. It can be implemented as a separate component, like an auto switch. When the circuit runs into an issue (like a short circuit), then the switch turns the circuit off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Cloud Performance Monitoring Architecture That Results in an Outage
&lt;/h2&gt;

&lt;p&gt;To check the performance of your architecture, you need to run load tests against your application. Below, you can see an architectural example that is based on Azure App Services and Azure Functions. The architecture contains the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Separated into four stages: Dev, Test, Staging, and Production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Login and monitoring are implemented with Prometheus and Grafana&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Loading tests are implemented with Azure DevOps and JMeter&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/azure-samples/jmeter-aci-terraform/tree/main/"&gt;Here&lt;/a&gt;, you can find an example of how to set up a &lt;strong&gt;JMeter&lt;/strong&gt; load test in Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XvxztHre--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2Ac9EEPdy11PwaPuzS" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XvxztHre--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2Ac9EEPdy11PwaPuzS" alt="df" width="880" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is an excerpt from DZone's 2021 Application Performance Management Trend Report.&lt;br&gt;
For more: &lt;a href="https://dzone.com/trendreports/application-performance-management-1"&gt;Read the Report&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Fixing the Issue
&lt;/h2&gt;

&lt;p&gt;As you can see in the diagram, everything looks good at first glance. Where do you think there could be an issue?&lt;/p&gt;

&lt;p&gt;You can see the fixed architecture of the diagram in Figure 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hOtNHYYH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AAi7hqv_rqVr0JXqy" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hOtNHYYH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AAi7hqv_rqVr0JXqy" alt="" width="880" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all, you should never run load testing against the Production stage as it may (and often will) cause downtime when you run an excessive load test. Instead, you should run the test against the Test or Staging environments.&lt;/p&gt;

&lt;p&gt;You can also create the replica of your production environment explicitly for load test purposes. In this case, you should not use real production data, since that may result in sending emails to real customers!&lt;/p&gt;

&lt;p&gt;Next, let's look at an application that should be architected under a high load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: High-Load Application Architecture on AKS
&lt;/h2&gt;

&lt;p&gt;Imagine that we have a popular e-Commerce application that needs to survive Black Friday.&lt;/p&gt;

&lt;p&gt;The application architecture should be based around the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Azure Kubernetes Services (AKS) with &lt;a href="https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler"&gt;Kubernetes Cluster Autoscaler&lt;/a&gt; are used as the main distributed environment and mechanism to scale compute power under load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Istio's service mesh is used to improve cluster observability, traffic management, and load balancing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Azure Log Analytics and Azure Portal Dashboards are used as a central logging system.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gP-l5IDM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AQU8dXreqls21JP5S" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gP-l5IDM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AQU8dXreqls21JP5S" alt="" width="880" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the figure above, you can see that the AKS cluster contains nodes that are represented as virtual machines under the hood. The Autoscaler is the main component here. It scales up the node count when the cluster is under high load. It also scales the node down to a standard size when the cluster is under normal load.&lt;/p&gt;

&lt;p&gt;Istio provides the data plane and control plane, assisting with the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Load balancing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TLS termination&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Service discovery&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Health checks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identity and access management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuration management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitoring, logs, and traffic control&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note that the architecture includes the stages Dev, Test, Staging, and Production, of course. The formula for the highly available Kubernetes cluster is having separate clusters per stage. However, for Dev and Test, you can use a single cluster separated by namespaces to reduce infrastructure costs.&lt;/p&gt;

&lt;p&gt;For additional logging reinforcement, we used Azure Log Analytics agents and the Portal to create a dashboard. Istio contains a lot of &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/"&gt;existing metrics&lt;/a&gt;, including those for performance and options to customize them. You can then integrate with a &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/using-istio-dashboard/"&gt;Grafana&lt;/a&gt; dashboard.&lt;/p&gt;

&lt;p&gt;Lastly, you can also set up a load test using Istio. &lt;a href="https://github.com/istio/tools/tree/release-1.11/perf/load"&gt;Here&lt;/a&gt; is a good example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top Open-Source Application Monitoring Tools
&lt;/h2&gt;

&lt;p&gt;Let's start off by listing some of the most popular open-source application performance tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://skywalking.apache.org/"&gt;Apache Sky Walking&lt;/a&gt; is a powerful, distributed performance and log analysis platform. It can monitor applications written in .NET Core, Java, PHP, Node.js, Golang, LUA, C++, and Python. It supports cloud integration and contains features like performance optimization, slow service and endpoint detection, service topology map analysis, and much more. See the feature map in the image below:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Figure 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fpmsd5ta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AyPLXHcl4Cv7g58gG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fpmsd5ta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3200/0%2AyPLXHcl4Cv7g58gG" alt="" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/scouter-project/scouter"&gt;Scouter&lt;/a&gt; is a powerful tool that can monitor Redis, Nginx, Kafka, MongoDB, Kubernetes, and other sources. It can monitor CPU, memory network and heap utilization, active users, active services, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/wgliang/goappmonitor"&gt;GoappMonotr&lt;/a&gt; is a tool that provides performance monitoring for Golang applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/pinpoint-apm/pinpoint"&gt;Pinpoint&lt;/a&gt; is a performance monitoring tool for Python, Java, and PHP applications. It can monitor CPU, memory, and storage utilization. You can integrate it into your project without changing a single line of code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/newrelic"&gt;Code Speed&lt;/a&gt; is a simple APM tool. It can be installed into your Python application to monitor and analyze the performance of your code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are tools like &lt;a href="https://github.com/DataDog"&gt;Datadog&lt;/a&gt; that have community licenses or trials. Also, if you are using Azure, you can enable Azure AppInsights with low cost or no cost at all, depending on bandwidth.&lt;/p&gt;

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

&lt;p&gt;In this article, we've dug into common performance mistakes and anti-patterns when working with distributed cloud-based architectures, introducing a checklist to consider when building applications that will face heavy loads. Also, we explored the open-source tools that can help with performance analysis, especially for projects on limited budgets. And, of course, we've covered a few examples of highly performant applications and an application that may have performance issues so that you, dear reader, can avoid these common performance mistakes in your cloud architectures.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;a href="https://dzone.com/trendreports/application-performance-management-1"&gt;https://dzone.com&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to boost your cloud and software architecture skills? Enroll on my hands-on courses: &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://boriszaikin-course.thinkific.com/courses/build-enterprise-application-with-multitenancy"&gt;&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S3O33Pp2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://import.cdn.thinkific.com/450020%252Fcustom_site_themes%252Fid%252FwVBTRSnSyWjPk0AaZRlC_Landing-Page-Header.jpeg" width="880" height="403"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://boriszaikin-course.thinkific.com/courses/build-enterprise-application-with-multitenancy"&gt;Building Enterprise applications with multitenancy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.educative.io/courses/event-driven-microservices-azure"&gt;&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UYbsXbiL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--71K_wkAl--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://miro.medium.com/max/875/1%252A2Bfh07YckDlPP8em-Oia_A.png" width="880" height="370"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.educative.io/courses/event-driven-microservices-azure"&gt;Building Event Driven and Microservices Architecture in Azure&lt;/a&gt;&lt;/p&gt;





</description>
      <category>cloud</category>
      <category>azure</category>
      <category>kubernetes</category>
      <category>security</category>
    </item>
    <item>
      <title>2 Widespread Attacks on Your Containerized Environment and 7 Rules to Prevent it.</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Fri, 23 Jul 2021 13:39:30 +0000</pubDate>
      <link>https://dev.to/boriszn/2-widespread-attacks-on-your-containerized-environment-and-7-rules-to-prevent-it-4h2b</link>
      <guid>https://dev.to/boriszn/2-widespread-attacks-on-your-containerized-environment-and-7-rules-to-prevent-it-4h2b</guid>
      <description>&lt;h2&gt;
  
  
  How to Apply Common Security Principles and What tool to Choose to Prevent Attacks on Your Docker Containers and Kubernetes clusters.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Man-in-the-Middle attack
&lt;/h3&gt;

&lt;p&gt;The MITM attack is widespread in the Kubernetes and Docker. This attack includes additional malicious parts between the component that sends data and the component that receives this data. It can be a fake container, service, middleware, or even a human. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-8554"&gt;CVE-2020–8554&lt;/a&gt; — vulnerability that allows attackers to obtain access to the cluster by creating ClisterIPs service.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://unit42.paloaltonetworks.com/siloscape"&gt;Siloscape&lt;/a&gt; — malware inside windows containers. The Silocape creates a backdoor to the whole Kubernetes cluster, including sensitive data and CPU, GPU, and resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Cryptojacking attack
&lt;/h3&gt;

&lt;p&gt;This attack allows an attacker to run the malicious code to use the CPU, GPU, and Memory of the PC for mining cryptocurrencies. Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2018-15664"&gt;CVE-2018–15664&lt;/a&gt; — gives access to the docker system with root permission.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The container itself is a small OS that can be susceptible to attack with malicious code. In this article, we talk about&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What you need to do to secure your app in the container.&lt;/li&gt;
&lt;li&gt;Security tools.&lt;/li&gt;
&lt;li&gt;Security rules, policies.&lt;/li&gt;
&lt;li&gt;How to apply common security principles to prevent attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I consider using Docker and Kubernetes as two current leaders in container engines and orchestration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing Docker Containers Principles and Rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Use Principle of Least Privileges
&lt;/h3&gt;

&lt;p&gt;This principle means that you should not execute containers using admin users. You should create users that have admin access and can only operate with this particular container. You can also make groups add users there. You can read more about it in &lt;a href="https://docs.docker.com/engine/security/userns-remap/#about-remapping-and-subordinate-user-and-group-ids"&gt;“Isolate containers with a user namespace”&lt;/a&gt;. Below is an example of how to create the user and group.&lt;/p&gt;

&lt;p&gt;First of all, you should use official verified and signed images. To find and check images you can use &lt;a href="https://docs.docker.com/engine/reference/commandline/trust_inspect/"&gt;docker trust inspect.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example docker trust inspect — pretty google/apigee-mart-server:1.3.6.&lt;/p&gt;

&lt;p&gt;Docker Content Trust (DCT) can help with digital signatures.&lt;/p&gt;

&lt;p&gt;It allows verification of images and publishers during runtime. This process is based on (Docker Content Trust Keys)[&lt;a href="https://docs.docker.com/engine/security/trust/#docker-content-trust-keys"&gt;https://docs.docker.com/engine/security/trust/#docker-content-trust-keys&lt;/a&gt;], which generates several keys during the first interaction with DCT.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Setup Resource Limits
&lt;/h3&gt;

&lt;p&gt;You should set up &lt;a href="https://docs.docker.com/config/containers/resource_constraints/#memory"&gt;Memory and CPU limits&lt;/a&gt; for your docker container because docker has a container that does not have this option by default. This principle is a way to prevent DoS attacks. For example, you can set up a Memory limit to prevent your container from consuming all memory. The same applies to CPU limits.&lt;/p&gt;

&lt;p&gt;There is also an option to set up resource limits on a Kubernetes level. I cover this topic in the Kubernetes Security section.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Secure and Understand Docker Networking
&lt;/h3&gt;

&lt;p&gt;This principle is essential to understand Docker’s networking principles. You should understand what &lt;a href="https://docs.docker.com/network/"&gt;Docker Network Drivers&lt;/a&gt; are, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bridge&lt;/li&gt;
&lt;li&gt;host&lt;/li&gt;
&lt;li&gt;overlay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, one container network stack does not have access to another container. However, if you configure bridged or host to accept traffic from any other containers or external networks, you can create a potential security backdoor for an attack. You can also disable inter container communication using just set flag &lt;code&gt;— icc=false&lt;/code&gt; within docker daemon.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Add Container Security Monitoring Architecture
&lt;/h3&gt;

&lt;p&gt;Security monitoring is essential for the detection of malicious code and attacks on your containers.&lt;/p&gt;

&lt;p&gt;With a proper monitoring tool, you should be able to detect the issues.&lt;/p&gt;

&lt;p&gt;The tool allows you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a real-time dashboard.&lt;/li&gt;
&lt;li&gt;Set up alerts to send you messages via Email, SMS, or even your preferable chat platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To find the vulnerability in Docker containers, you can use&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/scan/#how-to-scan-images"&gt;&lt;code&gt;docker scan command&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google/cadvisor"&gt;CAdvisor&lt;/a&gt;, which is also a pretty powerful tool to monitor your container. Moreover, you can also run it in the Kubernetes cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker scan or Caadvisor is a simple solution that applies to only one particular container. For more complex scenarios, for example, when you run 50+ containers in the Kubernetes, you need complex monitoring tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; and &lt;a href="https://grafana.com/"&gt;Grafana&lt;/a&gt;. Prometheus is a logging component that “scrapes” information from your container and puts it into a data source. The data sources can be SQL, NoSQL data storage. Also, Prometheus has an Alert Manager component. It allows users to create rule-based alerts. Grafana is a framework that helps to build complex UI dashboards. The dashboards can be easily configured to get data from Prometheus.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://datadoghq/"&gt;Datadog&lt;/a&gt;, which is a comprehensive, all-in-one monitoring tool that contains logging component subsystems and sidecars. It also includes a complex and interactive UI framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/azure/azure-monitor/containers/containers"&gt;Azure Log Analytics&lt;/a&gt;. It is a complex tool that monitors your containers, especially in Azure Container Registry, Azure Kubernetes Services. It is a handy option if you have your container solution under Azure Cloud Services as it supports it out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have listed some pretty popular solutions. However, there are many others in the market worth mentioning, like &lt;a href="https://sysdig.com/partners/docker/"&gt;Sysdig&lt;/a&gt;, &lt;a href="https://sematext.com/docker/"&gt;Sematext&lt;/a&gt;, &lt;a href="https://hub.docker.com/_/dynatrace"&gt;Dynatrace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I like to “cook” using the combination of &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; + &lt;a href="https://github.com/google/cadvisor"&gt;CAdvisor&lt;/a&gt; + &lt;a href="https://grafana.com/"&gt;Grafana&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus is a powerful and open-source option for monitoring CPU, GPU, Memory, Images, and other metrics.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google/cadvisor"&gt;CAdvisor&lt;/a&gt; is quite good at detecting vulnerabilities.&lt;/li&gt;
&lt;li&gt;Grafana is good at building, configuring dashboards and alerts, and imports all components together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, I use the security tool &lt;a href="https://github.com/aquasecurity/kube-bench"&gt;Kube-bench&lt;/a&gt; that covers vulnerabilities scanning only. The Kubebench brings an additional layer to your cluster security monitoring. There are plenty of security tools available for Kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E_RS-hhz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2A76zusSAV45VkNVzW" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E_RS-hhz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2A76zusSAV45VkNVzW" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Avoid putting Sensitive data in Docker Images.
&lt;/h3&gt;

&lt;p&gt;This principle is quite important to move all sensitive data out of the container. You can use different options to manage your secrets and other sensitive data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/swarm/secrets/"&gt;Docker secret&lt;/a&gt; allows you to store your secrets outside of the image.&lt;/li&gt;
&lt;li&gt;If your run docker containers in Kubernetes, you can use Secrets to store your passwords, certificates, or any other sensitive data&lt;/li&gt;
&lt;li&gt;Use cloud-specific storage for sensitive data — for example, Azure Key Vault or AWS Secret Manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Involve Vulnerability Scanning Tools
&lt;/h3&gt;

&lt;p&gt;Vulnerability scanning tools are the essential part of detecting images that may have security holes. Moreover, you can also integrate properly selected tools into the CI/CD process. Below I have listed some scanning tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/eliasgranderubio/dagda/"&gt;Dagda&lt;/a&gt; uses a static analysis approach to find viruses, malware, and fake sub-images and trojans. It is based on Red Hat Security Advisories (RHSA) libraries of existing vulnerabilities databases.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aquasecurity/trivy"&gt;Trivy&lt;/a&gt; can detect complex vulnerabilities with high accuracy for OS like Alpine Linux and RHEL/CentOS , Debian, Ubuntu, and others. It is quite powerful, opensource, and free. You can run Trivi in standalone or client/server modes. Therefore you can add it to your CI/CD process.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/quay/clair"&gt;Clair&lt;/a&gt; is used for static analysis of your images. It supports images that are based on the Open Container Initiative (OCI). You can build your services for scanning images that can be based on Clair API. Clair uses &lt;a href="https://cve.mitre.org/"&gt;CVE databases&lt;/a&gt; to detect vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find more in the &lt;a href="https://cve.mitre.org/"&gt;Containers Trend Report&lt;/a&gt; article.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Secured Docker Registries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To protect your images, you can create an additional security layer and use images from protected registries like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://goharbor.io/"&gt;Harbor&lt;/a&gt;, which is an open source registry with integrated vulnerability scanning. It is based on security policies that apply on docker artifacts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://quay.io/"&gt;Quay&lt;/a&gt;, which scans your images for vulnerability. It is an image registry powered by RedHat. Quay also offers a standalone image repository that you can install and use internally in your organization. Below you can see how it scans for vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what if you are already using other registries like Azure Container Registry or Docker Hub? You can find how to do it in the &lt;a href="https://dzone.com/trendreports/containers-1"&gt;Containers Tend Report article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Securing Kubernetes
&lt;/h3&gt;

&lt;p&gt;Kubernetes security is a big and important topic. The main security rules are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Networking and Network Policies. You should understand how the Kubernetes networking model works. It will help you set up proper network communication between pods, and pretend creating open ports or direct access to the nodes. The Network Policy can help you to organize this communication.&lt;/li&gt;
&lt;li&gt;Secure Ingress and Egress traffic to your pod. Here you can also use Network Policies. You can use strategy to deny all Egress and Ingress traffic and then start opening. You can also use service mesh like &lt;a href="https://istio.io/latest/docs/concepts/what-is-istio/"&gt;Istio&lt;/a&gt;. It adds additional service layers, automates traffic and helps with monitoring. However, you should be careful to use the service mesh as it may add additional complexity.&lt;/li&gt;
&lt;li&gt;Transport Layer Security. You should enable TLS if it is not opened. TLS should be used for communication between Kubernetes cluster services.&lt;/li&gt;
&lt;li&gt;Restrict Access To Kubernetes Dashboard.&lt;/li&gt;
&lt;li&gt;Use RBAC and follow the principle of least privilege.&lt;/li&gt;
&lt;li&gt;Restrict access to Kubelet. You should enable the authentication and authorization to use this tool. Only admins should have access to Kubelet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The security principles that are mentioned in the docker container section&lt;/p&gt;

&lt;h3&gt;
  
  
  Detect Kubernetes Configuration and Security Issues
&lt;/h3&gt;

&lt;p&gt;To find security and misconfiguration in kubernetes, you can use the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/aquasecurity/kube-bench"&gt;Kube-bench&lt;/a&gt;. It is one of the most powerful tools in the section. Its security check is based on CIS Kubernetes Benchmark.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shopify/kubeaudit"&gt;Kubeaudit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubesec.io/"&gt;Kubesect.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Security Topic is very important and complex, especially in the Docker and Kubernetes world. This article contains the most important recommendations to be taken into account. You can find more information about Containers Trends in the &lt;a href="https://dzone.com/trendreports/containers-1"&gt;Containers Tend Report.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>containers</category>
      <category>kubernetes</category>
      <category>cloudcomputing</category>
      <category>security</category>
    </item>
    <item>
      <title>Need to Build Event-Driven Architecture in the Azure? Easy with Azure Event Grid and Azure function!</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Wed, 24 Mar 2021 11:48:52 +0000</pubDate>
      <link>https://dev.to/boriszn/need-to-build-event-driven-architecture-in-the-azure-easy-with-azure-event-grid-and-azure-function-4igj</link>
      <guid>https://dev.to/boriszn/need-to-build-event-driven-architecture-in-the-azure-easy-with-azure-event-grid-and-azure-function-4igj</guid>
      <description>&lt;p&gt;Building and supporting infrastructure in a large organization can be a challenging task due to automation of all deployment steps, for example, creating cloud environments for a department with a set of virtual machines that contain preinstalled software, utilities, and services that may have their own dependencies like database and storage, connection to log analytics and other log services. Utilities and services may have external dependencies as well. Therefore you need to have a mechanism to check and ensure that required connections are established, dependencies are installed, proper automation runbook is triggered. The mechanism should alert the administrator of error, perform a backup.&lt;/p&gt;

&lt;p&gt;To build this mechanism, I will use the following Azure resources.&lt;/p&gt;

&lt;p&gt;Event Grid is a key component that acts as a main event processor and contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Topics are azure resources that represent components that generate events.&lt;/li&gt;
&lt;li&gt;Subscriptions/endpoint is azure resource that handles the event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relation between publisher components, events, topic, and subscriber/endpoint is shown in the diagram below. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JXmz207g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/868/1%2AFWCoi88lk_C0qtVnO6yq5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JXmz207g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/868/1%2AFWCoi88lk_C0qtVnO6yq5w.png" alt="eda-architecture" width="694" height="390"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Event grid contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dead Letter Queue and retry policy — if message not able to reach the Endpoint, then you should also configure the retry policy&lt;/li&gt;
&lt;li&gt;Event filtering — the rule allows the event grid to deliver specific event types to the endpoint point. For example: when you create a new VM in the topic container (resource group or subscription). The Event Grid will catch the event and deliver it to the endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--peAIC0Oc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Aykza4L4YTK2CvTtMLYQ5dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--peAIC0Oc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Aykza4L4YTK2CvTtMLYQ5dw.png" alt="eda-architecture" width="875" height="555"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Queue Storage Account
&lt;/h2&gt;

&lt;p&gt;Queue Storage uses as a main event storage. When the event is generated via Event Grid, the final destination will be a queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Function
&lt;/h2&gt;

&lt;p&gt;Functions are used as microservice that contains logic to validate required resources and connections. Each function may also contain a database for storing configuration or state management data.&lt;/p&gt;

&lt;p&gt;After a rather high-level architecture description, I will provide more details on it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lCiRc-oT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1250/1%2Auuzp_UtSFGcgmWFbuYhSEw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lCiRc-oT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1250/1%2Auuzp_UtSFGcgmWFbuYhSEw.jpeg" alt="eda-architecture" width="880" height="354"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;As you have noticed, an event grid is linked with a subscription listening on the events related to new virtual machine creation. It is necessary to add filtering here. Otherwise, the event grid will generate messages whenever any resource is created in a subscription or the target resource group. All events delivered are delivered in the storage queue. In my project, I’m using three queues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main queue is a destination for all messages from the event grid.&lt;/li&gt;
&lt;li&gt;Retry queue receives all messages which failed during the first steps of validation and were scheduled for future retry.&lt;/li&gt;
&lt;li&gt;Succeeded queue is used for all successfully processed messages. In my project, I used this queue for future statistics and reporting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, a storage account is linked to Azure Log Analytics to synchronize all logs and alerts. For example, log analytics will log this as an error alert administrator if there are more messages in the retry queue than expected.&lt;/p&gt;

&lt;p&gt;The next component is the Azure function app contains several azure functions with validation, message processing, and logic to trigger the runbook.&lt;/p&gt;

&lt;p&gt;Validation Function is linked with the main event queue. When the event grid sends a message to the main queue, the function is automatically triggered.&lt;/p&gt;

&lt;p&gt;Retry Function is based on a timer trigger and will be run constantly to check failed messages intended to retry (in retry event queue).&lt;/p&gt;

&lt;p&gt;API Function(HTTP trigger) is intended to trigger (re-run) the whole process from runbook, Admin UI, etc.&lt;/p&gt;

&lt;p&gt;The whole functions workflow is described below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DwpZrhox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AAv7ehBaBLgBd5RGHQncRtw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DwpZrhox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AAv7ehBaBLgBd5RGHQncRtw.jpeg" alt="eda-architecture" width="875" height="665"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture Code Base
&lt;/h1&gt;

&lt;p&gt;The function app is written on PowerShell (Powershell Class) and uses the principles of OOP. This approach allows us to build modular well-supported code and add or remove functions at any time.&lt;/p&gt;

&lt;p&gt;Why did I opt for PowerShell and not c# or JavaScript? The main reason is that a solution based on PS can be supported not only by developers but also by cloud administrators and system engineers. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this article, I described building event-driven architecture to manage the virtual machine, related utilities, and components.&lt;/p&gt;

&lt;p&gt;You can reuse the presented solution in the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key Vaults and SSL certificate management (check certificate expiration time, log and inform, update certificate automatically)&lt;/li&gt;
&lt;li&gt;Create custom logic to build cloud expense reports&lt;/li&gt;
&lt;li&gt;Cloud resources backup, check availability and log (using Log Analytics or other tools)&lt;/li&gt;
&lt;li&gt;Resource clean up management&lt;/li&gt;
&lt;li&gt;Container management solution&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  - &lt;strong&gt;&lt;a href="https://github.com/Boriszn/AzureValidationMiddleware"&gt;Here&lt;/a&gt;&lt;/strong&gt; you can find Source Code
&lt;/h4&gt;

&lt;h4&gt;
  
  
  - And &lt;strong&gt;&lt;a href="https://itnext.io/event-driven-and-microservices-architecture-with-azure-function-and-azure-event-grid-51bd612d99b6"&gt;here&lt;/a&gt;&lt;/strong&gt; the complete tutorial article.
&lt;/h4&gt;

&lt;h1&gt;
  
  
  Bonus
&lt;/h1&gt;

&lt;p&gt;As this topic quite useful and popular in the world of Cloud and Software architecture. Moreover, these approaches solve a lot of business problems. Taking this into account, I’ve decided to create a practical, interactive course. It is based on my experience building EDA architecture for enterprise customers.&lt;/p&gt;

&lt;p&gt;You can play with code, test architecture, and run pipelines directly in the course! Below you can find a link and enroll in the course.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.educative.io/courses/event-driven-microservices-azure"&gt;Building Event Driven and Microservices Architecture in Azure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microservices</category>
      <category>eventdrivenarchitecture</category>
      <category>serverless</category>
      <category>azure</category>
    </item>
    <item>
      <title>Building a Bot as an Alternative to Classic User Interface. Why is it better?</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Mon, 22 Mar 2021 09:39:34 +0000</pubDate>
      <link>https://dev.to/boriszn/building-a-bot-as-an-alternative-to-classic-user-interface-why-is-it-better-2mfe</link>
      <guid>https://dev.to/boriszn/building-a-bot-as-an-alternative-to-classic-user-interface-why-is-it-better-2mfe</guid>
      <description>&lt;p&gt;My team and I were working on an open-source customer project in the aerospace domain that provided scientific data for students and university data scientists. After several attempts to build a classic Web UI application, we moved to a simplified Bot Flow. And this is why and how.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RdHA6Bbz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2A4pGojA07FY195Ldyfsx_RQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RdHA6Bbz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2A4pGojA07FY195Ldyfsx_RQ.jpeg" alt="alt text" title="nasa-bot" width="750" height="913"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project has a short and straightforward data response. It contains only essential data like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coordinates.&lt;/li&gt;
&lt;li&gt;Event time frames.&lt;/li&gt;
&lt;li&gt;Source type, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, some messages have links to the detailed report that is a classic Web UI. Having a chat with a bot is more natural as you feel like you are communicating with a person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Healthcare Bot
&lt;/h2&gt;

&lt;p&gt;Here is another good example of how bot service can be convenient in our daily life. Recently Microsoft has launched the Healthcare Bot Service.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.microsoft.com/en-us/research/project/health-bot/"&gt;Overview of the Healthcare Bot service.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/health-bot/"&gt;Technical documentation that includes architecture overview, how to build it, and service concepts.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V8XFZewv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AwnbMVJ1SJuCY3BuPgFQpyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V8XFZewv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AwnbMVJ1SJuCY3BuPgFQpyw.png" alt="alt text" title="nasa-bot" width="815" height="1118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I build a simplified version of a Bot that aggregates data from NASA API and provides the user with a simple workflow and short data feedback. Also, I use Azure Language Recognition (LUIS) service to improve and simplify the conversation flow.&lt;/p&gt;

&lt;p&gt;The NASA Bot contains the following functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gather data from the NASA API.&lt;/li&gt;
&lt;li&gt;Aggregate and simplify data payload.&lt;/li&gt;
&lt;li&gt;Recognize the user input with Azure Language Recognition (LUIS). We will go into the LUIS below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we dive deep into the details, let’s examine why the Bot is better than a classic UI.&lt;/p&gt;

&lt;p&gt;Using the bot approach may significantly improve the user experience if you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplified and short data payload.&lt;/li&gt;
&lt;li&gt;Intuitive user interaction flow that looks like a Q&amp;amp;A dialog.&lt;/li&gt;
&lt;li&gt;No complex UI.&lt;/li&gt;
&lt;li&gt;Focus on content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UI is better than a Bot if you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex data payload.&lt;/li&gt;
&lt;li&gt;Complex UI forms (Admin Portals, Editors, Maps, News Portals, Calendars).&lt;/li&gt;
&lt;li&gt;Complex data visualization (Dashboards, Charts).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best option may be to combine both approaches and use all benefits from a Bot and a classic UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why NASA API?
&lt;/h2&gt;

&lt;p&gt;I have chosen NASA API because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A granular and quite simple API design.&lt;/li&gt;
&lt;li&gt;Well-documented API calls.&lt;/li&gt;
&lt;li&gt;Easy and fast registration process.&lt;/li&gt;
&lt;li&gt;API key request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ihiFLxQz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AKf4KVcUskCmviWHy1CDkCg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ihiFLxQz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AKf4KVcUskCmviWHy1CDkCg.jpeg" alt="alt text" title="nasa-bot" width="875" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NASA API is divided into categories or sub APIs, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Space Weather Database Of Notifications, Knowledge, Information (DONKI)&lt;/li&gt;
&lt;li&gt;Mars Weather Service API&lt;/li&gt;
&lt;li&gt;Two-line element data earth-orbiting objects at a given point in time (TLE API)&lt;/li&gt;
&lt;li&gt;Satellite Situation Center&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=""&gt;NASA API with other calls.&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Why Azure Bot Service?
&lt;/h1&gt;

&lt;p&gt;Azure Bot Service, alongside the Bot Framework, provides you with an opportunity to build, test, store your Bot. Bot Framework includes tools, SDK, AI services, and templates that allow you to make a bot quite quickly.&lt;/p&gt;

&lt;p&gt;In my case, I have created a project with Visual Studio using the template and installed a toolset, SDK, and templates. I have used .NET Core and C#. However, this framework also allows using JavaScript and Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LUIS?
&lt;/h2&gt;

&lt;p&gt;LUIS is a language understanding service that allows you to build and train your Bot to understand a natural language. I have included LUIS in my Bot to provide people with information from NASA API using different natural language construction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&amp;amp;tabs=csharp"&gt;More information about LUIS.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the current project, I have trained LUIS manually using the LUIS Portal. Below you can see that I added language sentences to train the LUIS to recognize user feedback and set the score. The LUIS portal also contains an option that allows you to test your data set and deploy it to different stages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9tAKEIYB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2ALmBZPKWNLxUe1GtDkNGI5A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9tAKEIYB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2ALmBZPKWNLxUe1GtDkNGI5A.jpeg" alt="nasabot" title="nasabot" width="875" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xqd61AmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AE7GYXDqGDcImX0OVt8ImGA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xqd61AmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AE7GYXDqGDcImX0OVt8ImGA.jpeg" alt="nasabot" title="nasabot" width="875" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, a manually trained model is suitable for small and middle-level bot projects. You should consider using pre-trained models that you quickly find in JSON format or set up for the production application.&lt;/p&gt;

&lt;p&gt;You can also improve language understanding by adding &lt;a href="https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-qna?view=azure-bot-service-4.0&amp;amp;tabs=cs"&gt;QnA (Question and Answers)&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/azure/bot-service/language-generation/bot-builder-howto-use-lg-templates?view=azure-bot-service-4.0&amp;amp;tabs=cs"&gt;language generation templates&lt;/a&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  NASA Bot Architecture Overview
&lt;/h1&gt;

&lt;p&gt;The NASA Bot architecture represents an N-tire structure based on an MVC pattern. The Bot includes API Controllers, Models, and Services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--svpYG4un--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AnjOoajhI3IRGXNIlrYvBhA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--svpYG4un--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AnjOoajhI3IRGXNIlrYvBhA.jpeg" alt="nasabot" title="nasabot" width="875" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The NASA bot consists of the following main components.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nasa bot class is a component that contains the conversation flow event handlers like: MembersAdded and MessageActivity. And it also serves as an entry point to start a conversation flow.&lt;/li&gt;
&lt;li&gt;External API aggregation service joins logic related to language recognition and fetches data from NASA API.&lt;/li&gt;
&lt;li&gt;NasaApi library subproject is a wrapper of Nasa API calls. It contains logic to obtain, parse data from API and build a proper response for the business layer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can find &lt;a href="https://itnext.io/building-a-bot-as-an-alternative-to-classic-user-interface-why-is-it-better-6ce4fd6fdabb"&gt;code examples and the NASA Bot project here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is the list of other bot components with a link to the GIT repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API (and BotController) is an endpoint to the Bot, and when you create your Bot with Visual Studio or VS Code, it creates a project that looks almost like a Web API project. The Web API is based on an MVC pattern.&lt;/li&gt;
&lt;li&gt;LanguageRecognitionService connects the Bot to the LUIS API and triggers the language recognition process.&lt;/li&gt;
&lt;li&gt;ObservationTypeModel contains all fields related to LUIS Response, including maximum intent score calculation logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conversation workflow
&lt;/h2&gt;

&lt;p&gt;Below, I have added several screenshots that demonstrate an interaction with the Bot. As you see, here I used Bot Framework Emulator. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dn6tIFUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2A1IA_zfMKn0K2Qb9ZoNhhUQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dn6tIFUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2A1IA_zfMKn0K2Qb9ZoNhhUQ.jpeg" alt="nasabot" title="nasabot" width="875" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zXmg6bQ9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AxQ5xo9qYd2G_JsHXEkw_JQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zXmg6bQ9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2AxQ5xo9qYd2G_JsHXEkw_JQ.jpeg" alt="nasabot" title="nasabot" width="875" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The article demonstrates the main advantages that bot services can bring in comparison to UI interfaces. In the next article, I will extend the Bot by adding more advanced features.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/Boriszn/NASAbot"&gt;link to the git repository&lt;/a&gt; where you can clone the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;In the next article, I will upgrade the Bot by adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QnA maker and models&lt;/li&gt;
&lt;li&gt;Unit Tests&lt;/li&gt;
&lt;li&gt;Other APIs&lt;/li&gt;
&lt;li&gt;Improved language recognition (Involve language generation)&lt;/li&gt;
&lt;li&gt;Bot deployment across multiple channels (Telegram, Slack)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please feel free to suggest your ideas on what else should be added to the Bot or covered in the next article. Looking forward to hearing from you in the comments.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azurebotservices</category>
      <category>cloud</category>
      <category>nasa</category>
    </item>
    <item>
      <title>Easy and Fast Adjustment of Kubernetes CPU and Memory</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Wed, 17 Feb 2021 13:39:12 +0000</pubDate>
      <link>https://dev.to/boriszn/easy-and-fast-adjustment-of-kubernetes-cpu-and-memory-3m3p</link>
      <guid>https://dev.to/boriszn/easy-and-fast-adjustment-of-kubernetes-cpu-and-memory-3m3p</guid>
      <description>&lt;p&gt;Assigning and Managing CPU and Memory resources in the Kubernetes can be tricky and easy at the same time. Having done this task for numerous customers, I have decided to create a framework zone. I will show you what Kubernetes resources and limits are and how to manage them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9iaM2I6g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1350/1%2A0raPh3cQoHTCqatZDBQA5A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9iaM2I6g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1350/1%2A0raPh3cQoHTCqatZDBQA5A.png" alt="logo-header" title="Log Header" width="880" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The framework contains the following steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infographic Guide shows what algorithms to follow to determine and assign the resources and limits.&lt;/li&gt;
&lt;li&gt;Code templates allow applying those algorithms with minimal adaptation.&lt;/li&gt;
&lt;li&gt;Algorithms and tools to gather the metrics about resource consumption and set up the limits.&lt;/li&gt;
&lt;li&gt;Links to the official documentation where you can quickly grab some examples and read more detailed information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this article doesn’t contain.
&lt;/h2&gt;

&lt;p&gt;My goal here is simplicity. So you won’t find detailed descriptions of how resources, limit ranges, and quotas work. There are plentiful articles written about it, as well as Kubernetes thorough documentation. Instead, here you will find information &lt;strong&gt;on how to quickly start adjusting Kubernetes resources in your projects.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Basics Guide on CPU and Memory Resources
&lt;/h1&gt;

&lt;p&gt;Two cheat-sheets explaining what resources are in terms of Kubernetes, measurement units, the resource state workflow, and some rules on how to apply it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gKWg3Cnk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/1%2AyjMSe7VDWmTxNogGcQxytQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gKWg3Cnk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/1%2AyjMSe7VDWmTxNogGcQxytQ.png" alt="logo-header" title="req-limits" width="880" height="708"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Basics Guide on CPU and Memory Resources
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4cSZ2TIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/0%2AOxT-5PQk8GyGxdsh" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4cSZ2TIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/0%2AOxT-5PQk8GyGxdsh" alt="logo-header" title="req-limits" width="880" height="813"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Be aware that the CPU calculation formula is not applied to every setup or project. It is used as a starting point in the process of detecting and assigning the resource and limits.&lt;/p&gt;

&lt;p&gt;Kubernetes deployment &lt;a href=""&gt;YAML template&lt;/a&gt; includes container, resource, and limits definitions.&lt;/p&gt;

&lt;p&gt;More detailed information and code snippets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/"&gt;Kubernetes.io. Assign memory resource guide.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/"&gt;Kubernetes.io. Assign CPU resource guide.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"&gt;Kubernetes best practices resource requests and limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/aks/developer-best-practices-resource-management"&gt;Azure Kubernetes Services. Developer best practices resource-management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Resources Quota Guide
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJeVpiHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/0%2A8GQuYKVTclvqe-R1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJeVpiHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/0%2A8GQuYKVTclvqe-R1" alt="logo-header" title="req-limits" width="880" height="793"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Two &lt;a href=""&gt;quota templates&lt;/a&gt; that contain quotas definition for persistent volume claims and resources in the backed namespace.&lt;/p&gt;

&lt;p&gt;You can use kubectl “apply” command to set the quota constraints for a namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f resource-quota.yaml — namespace backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detailed explanation of quotas, metrics, and how to use quota expressions and code samples.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/policy/resource-quotas/"&gt;https://kubernetes.io/docs/concepts/policy/resource-quotas/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Limit Ranges Guide
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FlB_v_XW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/1%2Au4HOj5vwpS5PV6x8Y9caNg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FlB_v_XW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1500/1%2Au4HOj5vwpS5PV6x8Y9caNg.png" alt="logo-header" title="Log Header" width="880" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two limit range code templates to limit CPU and Memory resources for the containers, and to limit the storage (Persistent Volume Claim). To set this constraint, you can also use kubectl apply -f path/to/file.yaml -n namespace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: LimitRange
metadata:
  name: backend-limit-range
spec:
  limits:
  - default:
      memory: 110Mi
      cpu: 500m
    defaultRequest:
      memory: 20Mi
      cpu: 100m
    type: Container

--
apiVersion: v1
kind: LimitRange
metadata:
  name: backend-storage-limits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 5Gi
    min:
      storage: 2Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More detailed information and code snippets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//Manage%20resources.%20Cpu%20default%20namespace"&gt;https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/cpu-default-namespace/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//Kubernetes%20io.%20Limit%20storage%20consumption"&gt;https://kubernetes.io/docs/tasks/administer-cluster/limit-storage-consumption/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//Kubernetes%20io.%20Limit%20ranges"&gt;https://www.howtoforge.com/limit-ranges-in-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Tools and Frameworks that Helps with Resource and Limits Routine
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/derailed/popeye"&gt;Popeye&lt;/a&gt; scans your cluster for potential issues with configuration, resources, and network holes and generates detailed reports with all issues.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FairwindsOps/goldilocks/"&gt;Goldilocks&lt;/a&gt; scans pods for resource limits and creates reports with recommended resources.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure/kube-advisor"&gt;Kube-advisor&lt;/a&gt; simple tool, from the Azure team, that scans pods for missing resources and limits requests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/derailed/k9s#benchmark-your-applications"&gt;K9s+benchmark&lt;/a&gt; — provides a command-line interface (CLI) that allows you to easily manage, monitor, and even benchmark your cluster in your favorite terminal software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some useful free and open-source tools, which are easy to set up. You can also combine these tools with Datadog, Grafana+Prometeus, Azure Monitor to improve resource and limits monitoring.&lt;/p&gt;

&lt;p&gt;Some useful free and open-source tools, which are easy to set up. You can also combine these tools with Datadog, Grafana+Prometeus, Azure Monitor to improve resource and limits monitoring. &lt;/p&gt;

&lt;h1&gt;
  
  
  Algorithm
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Setup Resource Requests. Get information about CPU and Memory usage of specific Application/Container.&lt;/li&gt;
&lt;li&gt;Setup Resource Limits. Setup Resource Requests. Run a load test to detect CPU and Memory of a container under the high load..&lt;/li&gt;
&lt;li&gt;Monitor containers CPU and Memory usage.&lt;/li&gt;
&lt;li&gt;Monitor persistent storage usage.&lt;/li&gt;
&lt;li&gt;Check if you can apply resource limit using limit ranges (if you have similar containers, storage set up)&lt;/li&gt;
&lt;li&gt;Apply limit range for storage if needed. Or use Quota (it is not recommended to apply Quota for production environment)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I have shared with you the framework I created and use on a daily basis while working with the Kubernetes requests and limits. I hope you will find it useful. Any ideas on how to make it better?&lt;/p&gt;

&lt;p&gt;Please feel free to share your feedback and suggestions in the comments.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cpumemory</category>
      <category>aks</category>
    </item>
    <item>
      <title>6 Easy Ways to Manage and Harden VM Images in Azure</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Wed, 23 Sep 2020 15:24:36 +0000</pubDate>
      <link>https://dev.to/boriszn/6-easy-ways-to-manage-and-harden-vm-images-in-azure-30e8</link>
      <guid>https://dev.to/boriszn/6-easy-ways-to-manage-and-harden-vm-images-in-azure-30e8</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EqS2iuFT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2488/1%2AAeUYi7TT4Xvmy5-qI0KVhA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EqS2iuFT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2488/1%2AAeUYi7TT4Xvmy5-qI0KVhA.png" alt="" width="880" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing VM Images may be a nightmare. Here are 6 simple approaches on how to seamlessly build, share, test, and copy images in Azure. And as a bonus, you also get how to build image notifications based on Event-Driven-Architecture.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;Whether you are managing 100 Virtual Machines or 1000+ that build and harden VM images, do you manually manage your images? If you do, you know that it is quite expensive and leads to hard-to-detect errors and potential security vulnerabilities.&lt;/p&gt;

&lt;p&gt;Here is how I approach this issue for my customers. My procedure of creating image management for Azure.&lt;/p&gt;

&lt;h1&gt;
  
  
  1.Building an image using the Azure Image Builder
&lt;/h1&gt;

&lt;p&gt;The Azure Image Builder is a service that allows you to create custom images with Azure CLI. The image creation based on the JSON template, example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "Microsoft.VirtualMachineImages/imageTemplates",
    "apiVersion": "2019-05-01-preview",
    "location": "&amp;lt;region&amp;gt;",
    "dependsOn": [],
    "tags": {
        "imagebuilderTemplate": "ubuntu1804",
        "userIdentity": "enabled"
            },
        "identity": {
            "type": "UserAssigned",
                    "userAssignedIdentities": {
                    "&amp;lt;imgBuilderId&amp;gt;": {}

                }
                },
    "properties": {

        "buildTimeoutInMinutes" : 80,

        "vmProfile": 
            {
            "vmSize": "Standard_D1_v2",
            "osDiskSizeGB": 30
            },

        "source": {
            "type": "PlatformImage",
                "publisher": "Canonical",
                "offer": "UbuntuServer",
                "sku": "18.04-LTS",
                "version": "latest"

        },
        "customize": [
            {
                "type": "Shell",
                "name": "RunScriptFromSource",
                "scriptUri": "https://raw.githubusercontent.com/danielsollondon/azvmimagebuilder/master/quickquickstarts/customizeScript.sh"
            }          

        ],
        "distribute": 
            [

            ]
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have to cut the original template as it is quite long. Here you can find full example.&lt;/p&gt;

&lt;p&gt;The ARM template is quite simple it contains following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identity section is required, you have to create Managed Identity for image builder to have an access to create and edit images&lt;/li&gt;
&lt;li&gt;VmProfile is to set up VM configuration plan&lt;/li&gt;
&lt;li&gt;Source allows you to specify base image parameters. I use the latest Ubuntu Server 18.04 LTS from Canonical&lt;/li&gt;
&lt;li&gt;The customization section allows you to specify VM hardening scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/virtual-machines/linux/image-builder-json"&gt;Here&lt;/a&gt; you can see the full list of the Image Builder options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;Templates and the process itself is easy to understand and it can be easily integrated with Azure DevOps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;The image builder is still in the review therefore it is not recommended using it in the production. Set up can be a bit difficult in comparison to Hashicorp Packer. (explain how to work with the Packer in the next section).&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Building an image using the Hashicorp Packer
&lt;/h1&gt;

&lt;p&gt;Hashicorp Packer is a multi-platform solution that allows building custom images based on JSON templates. The JSON templates are well-structured and based on an easy-to-understand object model. The JSON templates have just three root objects: Communicators, Builders, Provisioners, and Post-Processors. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://itnext.io/6-easy-ways-to-manage-and-harden-vm-images-in-azure-1ab933099e7e#ac9d"&gt;Here&lt;/a&gt; you can find the JSON template.&lt;/p&gt;

&lt;p&gt;JSON template creates the VHD image of Ubuntu with a preinstalled NGINX web server and other updates. Here you can find a lot of other templates.&lt;/p&gt;

&lt;p&gt;To set up packer you can use Chocolatie package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco install packer -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run the JSON template using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packer build &amp;lt;path/your/template.json&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before your run the template you need to create Management Identity or Service Principal with proper permissions, For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az ad sp create-for-rbac -n "ImageContributor" 
 --role contributor `    
 --scopes /subscriptions/&amp;lt;subscription_id&amp;gt; `     
 --sdk-auth &amp;gt; az-principal.auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command automatically creates JSON file with clientId, clientSecret, tenantId, subscriptionId and other fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;The Packer and JSON templates simple to understand. They allow you to quickly set up the environment and start building images. There is also a strong community. Plus, the Packer supports multiple cloud providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;I have not found any serious disadvantages. However, while building an image, the packer always removes the disk that is required in some operations with images. For example, for copying an image.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Sharing images
&lt;/h1&gt;

&lt;p&gt;To share images in Azure, you may use the Shared Image Gallery. It can:&lt;br&gt;
    - create image definition&lt;br&gt;
    - keep versions of an image&lt;br&gt;
    - share an image&lt;/p&gt;

&lt;p&gt;For example, you can share an image across your Azure subscriptions, resource groups, and tenants.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Whrsds5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Aiw6io89o7n0kquGB0DpY2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Whrsds5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Aiw6io89o7n0kquGB0DpY2g.png" alt="img" width="875" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the current scenario, you can create a user group or a single user/service principal, assign contributor rights only for this shared image gallery.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az ad sp create-for-rbac -n "ImageContributor" 
 --role contributor `    
 --scopes /subscriptions/&amp;lt;subscr-id&amp;gt;/resourceGroups/sig-we-rg/providers/Microsoft.Compute/galleries/testsig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, users from the group can create the Virtual Machine based on the Ubuntu image from this Shared Image Gallery in the different subscriptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az vm create `
  --resource-group myResourceGroup `
  --name UbuntuVM`
  --image "/subscriptions/&amp;lt;subscr-id&amp;gt;/resourceGroups/sig-we-rg/providers/Microsoft.Compute/galleries/testsig/images/ubuntu-server-image-def/versions/1.0.0" \
  --admin-username azureuser \
  --generate-ssh-keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;The Azure Shared Image gallery easily allows you to build, share, manage, and customize images within your organization. SIG has Azure CLI, so you can easily automate image distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;The image remains in a shared access gallery. Thus, it physically stays there. So, when you share it across subscriptions, you cannot change or remove it independently for each subscription.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Copying images
&lt;/h1&gt;

&lt;p&gt;By copying images, you can deliver images from one subscription to another or from one resource group to another. All copied images are independent of each other. You can copy images using Azure Image Copy extension, or implement manual copying using Go. Let us have a look at the examples below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying image with Az Copy Extension
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ma697tTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2As1IjvLgobxhjP5COfQrRdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ma697tTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2As1IjvLgobxhjP5COfQrRdg.png" alt="img" width="875" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To copy images between subscriptions, I use Az Image Copy extension. It creates a new image (from the source image) in Resource Group A.&lt;/p&gt;

&lt;p&gt;Install copy extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az extension add --name image-copy-extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az image copy --source-resource-group Azure-Resource-Group-B `
              --source-object-name image-version-1.0 `
              --target-resource-group Azure-Resource-Group-A `
              --target-location westeurope `
              --target-subscription 111111-2222-2222-0000-0000000 ` # Subscription A
              --cleanup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important. Packer removes the disk automatically after the managed image is created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;Az Image Copy extension is simple to use and automate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;An image must contain a managed disc, otherwise the copy process fails with ‘Resource not found’ error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy image manually
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nTjav56P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Au4v0hRO0Ej_wAflvnImvcQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nTjav56P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/1%2Au4v0hRO0Ej_wAflvnImvcQ.png" alt="img" width="875" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If Az Copy Image Extension does not work for you, you can create a VHD image in the storage account and copy it to another destination storage account.&lt;/p&gt;

&lt;p&gt;The process workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create 2 Storage accounts, Source and Destination&lt;/li&gt;
&lt;li&gt;Create the VHD image in the Source storage account. You can use the Packer script from the first section.&lt;/li&gt;
&lt;li&gt;Generate Shared Access Signature for the VHD Source image. Here you can find an example of how to do it with Azure CLI.&lt;/li&gt;
&lt;li&gt;And copy the image. To demonstrate I use the following Go script. You can also use the AzCopy tool, &lt;a href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/disks-upload-vhd-to-managed-disk-powershell"&gt;here&lt;/a&gt; the example of how to do it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can find source code &lt;a href="https://itnext.io/6-easy-ways-to-manage-and-harden-vm-images-in-azure-1ab933099e7e#377c"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;The current image coping workflow is fully custom. Therefore, it can be changed at any time. Also, it can be useful when Az Image Copy extension does not work you. For example, when it removes managed disk while creating an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;You have to implement all workflow steps including:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- creating VHD
- generating and managing SAS token
- copying an image
- cleaning up
- converting an image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  5. Image and Disk Converting
&lt;/h1&gt;

&lt;p&gt;The operations to convert a VHD image to a Managed Disk, or a Managed Disk to a VHD image. This is useful when you need to distribute images across your organization with Azure Shared Image Gallery in the image copy process and when you need you to spin up a new VM.&lt;/p&gt;

&lt;p&gt;Also, it is useful when you have some legacy VHD and need to install updates, set up automatic backups, use availability sets, and availability zones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/virtual-machines/managed-disks-overview"&gt;Here&lt;/a&gt; is a list of advantages of the Managed Image.&lt;/p&gt;
&lt;h2&gt;
  
  
  Converting VHD image to the Managed Disk
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Provide the subscription Id where Managed Disks will be created
$subscriptionId = '00000-00000-0000-0000-0000000'

# Provide the name of your resource group where Managed Disks will be created. 
$resourceGroupName ='HBI'

# Provide the name of the Managed Disk
$diskName = 'image-test-disk'

# The Disk It should be greater than the VHD file size.
$diskSize = '130'

# Storage LRS. Options: Premium_LRS, Standard_LRS, etc
$storageType = 'Premium_LRS'

# Set Azure region (e.g. westus, westeurope), Ensure that location of the VHD image (alongside with Storage Account), 
# and future Managed Disk is the same.
$location = 'westeurope'

# Set URI of the VHD file (page blob) in a storage account.
$sourceVHDURI = 'https://imagesstorage.blob.core.windows.net/test-image-disk.vhd'

# Set the Resource Id of the Source storage account where VHD file is stored. You can avoid it if VHD in the same subscription
$storageAccountId = '/subscriptions/subscription-id/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/imagesstorage'

#Set the context to the subscription Id where Managed Disk will be created
Select-AzSubscription -SubscriptionId $SubscriptionId

$diskConfig = New-AzDiskConfig -AccountType $storageType -Location $location -CreateOption Import -StorageAccountId $storageAccountId -SourceUri $sourceVHDURI

New-AzDisk -Disk $diskConfig -ResourceGroupName $resourceGroupName -DiskName $diskName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Convert Managed Disk to Managed Image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$rgName = "HBI"
$location = "westeurope"
$imageName = "boriszn-test-imagefromdisk"


# Get disk 
$disk = Get-AzDisk -ResourceGroupName 'HBI' -DiskName 'image-test-disk' 
$diskId = $disk.Id

# Create Managed Image Config with Managed Disk Info 
# OS Types (Linux, Windows)
$imageConfig = New-AzImageConfig -Location $location
$imageConfig = Set-AzImageOsDisk -Image $imageConfig -OsState Generalized -OsType Linux -ManagedDiskId $diskId

# Create the image.
$image = New-AzImage -ImageName $imageName -ResourceGroupName $rgName -Image $imageConfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;With both cmdlets, you can deliver an image to your Azure Shared Image across different subscriptions and resource groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;The conversion process can be complicated because some images can be outdated, and the conversion process may fail.&lt;/p&gt;

&lt;h1&gt;
  
  
  6.Testing images
&lt;/h1&gt;

&lt;p&gt;To test images, you can simply spin-up the new VM from the image gallery and use an image definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az vm create `
   --resource-group container-image-rg `
   --name vm-test `
   --image "/subscriptions/&amp;lt;subscription-id&amp;gt;/resourceGroups/container-image-rg/providers/Microsoft.Compute/galleries/test-gallery/images/image-test-def" `
   --generate-ssh-keys 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;The command and process itself quite simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;It does not check whether specific software and services are installed correctly in the JSON configuration. So this logic has to be implemented separately.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Images Pub/Sub subsystem concept (Bonus)
&lt;/h1&gt;

&lt;p&gt;Managing images across several subscriptions or resource groups can be difficult, especially when you constantly producing new versions of images, or you have some automated process to spin-up new virtual machines.&lt;/p&gt;

&lt;p&gt;In this case, you need to build a notification system for notifying different components when a new image was created, a new version or image definition appears in the Azure Shared Image Gallery. You can easily create it using Azure Event Grid with filters.&lt;/p&gt;

&lt;p&gt;You can see how it can be done with the event grid, Queue, Webhook, and Azure Service Bus to deliver messages to the target component &lt;a href="https://itnext.io/6-easy-ways-to-manage-and-harden-vm-images-in-azure-1ab933099e7e#1f1f"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;That’s it. Based on these ways you can easily set up images Hardening and management pipeline in the Azure DevOps.&lt;/p&gt;

</description>
      <category>vm</category>
      <category>virtualmachinehardening</category>
      <category>azurecloudarchitecture</category>
      <category>systemarchitecture</category>
    </item>
    <item>
      <title>CI/CD as a Code for .NET Core application and Kubernetes using Azure DevOps +YAML.</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Mon, 25 May 2020 15:51:52 +0000</pubDate>
      <link>https://dev.to/boriszn/ci-cd-as-a-code-for-net-core-application-and-kubernetes-using-azure-devops-yaml-2k7a</link>
      <guid>https://dev.to/boriszn/ci-cd-as-a-code-for-net-core-application-and-kubernetes-using-azure-devops-yaml-2k7a</guid>
      <description>&lt;p&gt;Building CI/CD process for the .NET Core application could be complicated especially when you are dealing with Kubernetes and Docker and if you need to include code style analysis, unit test, and code coverage report.&lt;/p&gt;

&lt;p&gt;In this article I am going to explain the process of building a simple CI/CD pipeline for existing .net core application to moving it to Azure Kubernetes Services using Azure DevOps. The final Pipeline will be easy to understand and reusable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Architecture
&lt;/h2&gt;

&lt;p&gt;Deployment process includes the following steps: fetching the code from Git Repository, building application, restoring NuGet packages, running unit test, building unit test and code coverage reports, pushing docker image to registry and deploying to AKS cluster. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A65X8_od5-qGvJv5QwQArHw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A65X8_od5-qGvJv5QwQArHw.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Kubernetes Cluster setup
&lt;/h2&gt;

&lt;p&gt;I have used a simple AKS cluster, with 3 nodes architecture that contains &lt;a href="https://github.com/kubernetes/ingress-nginx" rel="noopener noreferrer"&gt;Ingress-nginx&lt;/a&gt; load balancer. You can also use &lt;a href="https://docs.traefik.io/v1.7/user-guide/kubernetes/" rel="noopener noreferrer"&gt;Traefik.io&lt;/a&gt;, &lt;a href="https://istio.io/docs/concepts/traffic-management/" rel="noopener noreferrer"&gt;istio.io&lt;/a&gt; even &lt;a href="https://docs.microsoft.com/en-us/azure/aks/load-balancer-standard" rel="noopener noreferrer"&gt;Standard Azure Load Balancer.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1162%2F1%2AkYV0Faixa372oq1SPsaHiQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1162%2F1%2AkYV0Faixa372oq1SPsaHiQ.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Detailed description of how to setup AKS cluster including Ingress setup and &lt;a href="https://github.com/Boriszn/DeviceManager.Api/tree/develop/aks-deployment" rel="noopener noreferrer"&gt;deployment scripts&lt;/a&gt; can be found &lt;a href="https://github.com/Boriszn/DeviceManager.Api/tree/develop#kubernates-minikube-cluster-setup" rel="noopener noreferrer"&gt;Here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After deploying AKS, the main resource group it will look like in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AY6HZv3mbZjU2f4PGork8vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AY6HZv3mbZjU2f4PGork8vg.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup service connections
&lt;/h2&gt;

&lt;p&gt;Before starting to configure the main pipeline steps the connection between Azure Container Registry(ACR) and Azure Kubernetes service needs to be granted by granting access of AKS service principal to ACR. RBAC service principal for Azure DevOps is created and everything is ready to push and pull docker images withing pipelines. Alternatively you can do it in Azure DevOps Service Connection which I will explain in the next session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$AksResourceGroup = '&amp;lt;rg-name&amp;gt;'
$AksClusterName = '&amp;lt;aks-cluster-name&amp;gt;'
$AcrName = '&amp;lt;acr-name&amp;gt;'
$AcrResourceGroup = '&amp;lt;acr-rg-name&amp;gt;'

# Get the id of the AKS service principal
ClientID=$(az aks show --resource-group $AksResourceGroup --name $AksClusterName --query "servicePrincipalProfile.clientId" --output tsv)

# Get the ACR registry resource id
AcrId=$(az acr show --name $AcrName --resource-group $AcrResourceGroup --query "id" --output tsv)

# Create role assignment
az role assignment create --assignee $ClientID --role acrpull --scope $AcrId

# Create a specific Service Principal for our Azure DevOps pipelines to be able to push and pull images and charts of our ACR
$ registryPassword=$(az ad sp create-for-rbac -n $acr-push --scopes $AcrId --role acrpush --query password -o tsv)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/aks/cluster-container-registry-integration" rel="noopener noreferrer"&gt;Here&lt;/a&gt; you can find a detailed description of how to configure the connection between ACR and AKS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure DevOps service connection with Azure Kubernetes Services and Azure Container Registry.
&lt;/h2&gt;

&lt;p&gt;By Using Service Connection you can connect Azure DevOps to your, already deployed AKS cluster, Azure Container Registry, Docker Registry (Docker Hub), and many other services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A8ghbiV4Y0y_TatPFH5vBYA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A8ghbiV4Y0y_TatPFH5vBYA.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The creation of connection to ACR is quite easy, you just need to specify a connection name, a subscription, and a registry name and that’s it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2Ah5cs10MlJ6Wp0g0R8BMoZg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2Ah5cs10MlJ6Wp0g0R8BMoZg.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can connect (and authenticate) your AKS cluster using Kubeconfig, Service Account and Azure Subscription. In my project I used Kubeconfig, as one of the fast option, because you need just find kubeconfig JSON, copy it and choose your Cluster context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AkyvIj-T_UNtEq-iv8EP_Sw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AkyvIj-T_UNtEq-iv8EP_Sw.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find KubeConfig it in the following directory (In the Windows): &lt;code&gt;C:\Users\your_user_name\.kube\config&lt;/code&gt; Here is documentation how to find and work with KubeConfig also for Linux and Mac.&lt;br&gt;
Pipeline steps&lt;/p&gt;

&lt;p&gt;Finlay I’m moving to pipeline steps and the very first step is to use a script for restoring all NuGet dependencies/packages, building dot net core application, Running Unit Tests, building code coverage report. Also please notice that I use system variable $(Build.BuildNumber) as tag for the coverage report generation. At the end test results will be published as artifacts and Azure DevOps can build visualization analytics charts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- script: |
    dotnet restore
    dotnet build ./src/DeviceManager.Api/ --configuration $(buildConfiguration)
    dotnet test ./test/DeviceManager.Api.UnitTests/ --configuration $(buildConfiguration) --filter Category!=Integration --logger "trx;LogFileName=testresults.trx"
    dotnet test ./test/DeviceManager.Api.UnitTests/ --configuration $(buildConfiguration) --filter Category!=Integration /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(System.DefaultWorkingDirectory)/TestResults/Coverage/
    cd ./test/DeviceManager.Api.UnitTests/
    dotnet reportgenerator "-reports:$(System.DefaultWorkingDirectory)/TestResults/Coverage/coverage.cobertura.xml" "-targetdir:$(System.DefaultWorkingDirectory)/TestResults/Coverage/Reports" "-reportTypes:htmlInline" "-tag:$(Build.BuildNumber)"
    cd ../../
    dotnet publish ./src/DeviceManager.Api/ --configuration $(buildConfiguration) --output $BUILD_ARTIFACTSTAGINGDIRECTORY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below you can see Tests blade with statistic data and Test Results in the Tests section of the Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2ANlT-WUdkgvgliuXika89pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2ANlT-WUdkgvgliuXika89pw.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AQ5VPhkpLDPtQ3AkcCBOvsQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AQ5VPhkpLDPtQ3AkcCBOvsQ.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 - Code coverage results
&lt;/h2&gt;

&lt;p&gt;The next step is to publish code coverage results to DefaultWorkingDirectory. The whole process is based on &lt;a href="https://github.com/danielpalme/ReportGenerator" rel="noopener noreferrer"&gt;Cubertura&lt;/a&gt; Report Generator, a .Net core library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: cobertura
    summaryFileLocation: $(System.DefaultWorkingDirectory)/TestResults/Coverage/**/*.xml
    reportDirectory: $(System.DefaultWorkingDirectory)/TestResults/Coverage/Reports
    failIfCoverageEmpty: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first statistic results are already available:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2ATp313_B1eiyItdnqumbK4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2ATp313_B1eiyItdnqumbK4g.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The detailed file by file report you can find on the Code Coverage tab. This report is based on generated xml reports. &lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 and 4 — Building Container and Pushing it to the ACR
&lt;/h2&gt;

&lt;p&gt;After the previous steps have been completed the project is to be put into a container. For this I am going to use version 1 Docker step. You need to specify a path to a Docker file like I did it here, provide image name, for example, &lt;code&gt;boriszn/devicemanagerapi&lt;/code&gt; and tag — &lt;code&gt;1.102.1&lt;/code&gt;, and the last step is to specify a container registry, for example, &lt;code&gt;devicemanagerreg.azurecr.io&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a tag creation I used semantic versioning and hard-coded some version numbers to simplify the Pipeline, however you can also use different approaches to build a tag, for example using variables that are required to run a pipeline or take then from git version.&lt;/p&gt;

&lt;p&gt;For a tag creation I used semantic versioning and hard-coded some version numbers to simplify the Pipeline, however you can also use different approaches to build a tag, for example using variables that are required to run a pipeline or take then from git version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- task: Docker@1
  displayName: 'Containerize the application'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    dockerFile: './src/DeviceManager.Api/Dockerfile'
    imageName: '$(fullImageName)'
    includeLatestTag: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to push our dockerised app to the Azure Container Registry. Here I specified command and image to push. You should take into account that I pushed 2 images with the first image tag &lt;code&gt;1.58338.4&lt;/code&gt; and the second one is tag: &lt;code&gt;latest&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- task: Docker@1
  displayName: 'Push image'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    command: 'Push an image'
    imageName: '$(fullImageName)'

- task: Docker@1
  displayName: 'Push latest image'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    command: 'Push an image'
    imageName: '$(imageName):latest'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AgUzSTjMzPWuf5jnoQymQGQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AgUzSTjMzPWuf5jnoQymQGQ.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AI6XttyHYxdWknaLYQn9_Mw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AI6XttyHYxdWknaLYQn9_Mw.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AKS deployment steps
&lt;/h2&gt;

&lt;p&gt;For Azure Kubernetes Deployment you need to replace a build number in AKS deployment YAML file and display it in Azure DevOps after successful execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- task: PowerShell@2
  displayName: 'Replace version number in AKS deployment yaml'
  inputs:
    targetType: inline
    script: |
        # Replace image tag in aks YAML
        ((Get-Content -path $(aksKubeDeploymentYaml) -Raw) -replace '##BUILD_ID##','$(imageTag)') | 
        Set-Content -Path $(aksKubeDeploymentYaml)
        # Get content
        Get-Content -path  $(aksKubeDeploymentYaml)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that run “Apply” command for AKS cluster.&lt;/p&gt;

&lt;p&gt;Two important parameters here are a path to my cluster deployment YAML script (./deployment/aks-deployment.yaml) and a cluster name (device-manager-api-aks). Other parameters were explained in the previous sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- task: Kubernetes@1
  displayName: 'kubectl apply'
  inputs:
    kubernetesServiceEndpoint: $(kubernetesServiceEndpoint)
    azureSubscriptionEndpoint: $(serviceConnection)
    azureResourceGroup: $(azureResourceGroupName)
    kubernetesCluster: $(aksClusterName)
    arguments: '-f $(aksKubeDeploymentYaml)'
    command: 'apply'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AHwYT_NiUvPZAbEawY7zUnA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AHwYT_NiUvPZAbEawY7zUnA.png" title="image" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it! Below I’ve listed complete YAML pipeline that we configured in the previous few sections and that is easy to import to your Azure DevOps projects. If you have any questions reach out in the comments!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trigger:
- develop

pool:
  vmImage: 'ubuntu-latest'

variables:
  imageName: 'boriszn/devicemanagerapi'
  buildConfiguration: 'Release'
  fullImageName: '$(imageName):$(imageTag)'
  containerRegistry: devicemanagerreg.azurecr.io
  imageTag: '1.$(build.buildId).4'
  serviceConnection: 'az-connect'
  azureResourceGroupName: 'boriszn-rg-aks-devicemanager-api-we'
  aksClusterName: 'device-manager-api-aks'
  aksKubeDeploymentYaml: './deployment/aks-deployment.yaml'
  kubernetesServiceEndpoint: 'device-managerapi-aks-connect'

steps:
- script: |
    dotnet restore
    dotnet build ./src/DeviceManager.Api/ --configuration $(buildConfiguration)
    dotnet test ./test/DeviceManager.Api.UnitTests/ --configuration $(buildConfiguration) --filter Category!=Integration --logger "trx;LogFileName=testresults.trx"
    dotnet test ./test/DeviceManager.Api.UnitTests/ --configuration $(buildConfiguration) --filter Category!=Integration /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(System.DefaultWorkingDirectory)/TestResults/Coverage/
    cd ./test/DeviceManager.Api.UnitTests/
    dotnet reportgenerator "-reports:$(System.DefaultWorkingDirectory)/TestResults/Coverage/coverage.cobertura.xml" "-targetdir:$(System.DefaultWorkingDirectory)/TestResults/Coverage/Reports" "-reportTypes:htmlInline" "-tag:$(Build.BuildNumber)"
    cd ../../
    dotnet publish ./src/DeviceManager.Api/ --configuration $(buildConfiguration) --output $BUILD_ARTIFACTSTAGINGDIRECTORY
- task: PublishTestResults@2
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: cobertura
    summaryFileLocation: $(System.DefaultWorkingDirectory)/TestResults/Coverage/**/*.xml
    reportDirectory: $(System.DefaultWorkingDirectory)/TestResults/Coverage/Reports
    failIfCoverageEmpty: false

- task: PublishBuildArtifacts@1

- task: Docker@1
  displayName: 'Containerize the application'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    dockerFile: './src/DeviceManager.Api/Dockerfile'
    imageName: '$(fullImageName)'
    includeLatestTag: true

- task: Docker@1
  displayName: 'Push image'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    command: 'Push an image'
    imageName: '$(fullImageName)'

- task: Docker@1
  displayName: 'Push latest image'
  inputs:
    azureSubscriptionEndpoint: $(serviceConnection)
    azureContainerRegistry: $(containerRegistry)
    command: 'Push an image'
    imageName: '$(imageName):latest' 

- task: PowerShell@2
  displayName: 'Replace version number in AKS deployment yaml'
  inputs:
    targetType: inline
    script: |
        # Replace image tag in aks YAML
        ((Get-Content -path $(aksKubeDeploymentYaml) -Raw) -replace '##BUILD_ID##','$(imageTag)') | 
        Set-Content -Path $(aksKubeDeploymentYaml)
        # Get content
        Get-Content -path  $(aksKubeDeploymentYaml)
- task: Kubernetes@1
  displayName: 'kubectl apply'
  inputs:
    kubernetesServiceEndpoint: $(kubernetesServiceEndpoint)
    azureSubscriptionEndpoint: $(serviceConnection)
    azureResourceGroup: $(azureResourceGroupName)
    kubernetesCluster: $(aksClusterName)
    arguments: '-f $(aksKubeDeploymentYaml)'
    command: 'apply'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>azure</category>
      <category>kubernetes</category>
      <category>pipelines</category>
      <category>dotnetcore</category>
    </item>
    <item>
      <title>Error handling and validation architecture in .NET Core</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Tue, 11 Dec 2018 17:05:47 +0000</pubDate>
      <link>https://dev.to/boriszn/error-handling-and-validation-architecture-in-net-core-3lhe</link>
      <guid>https://dev.to/boriszn/error-handling-and-validation-architecture-in-net-core-3lhe</guid>
      <description>&lt;p&gt;In many projects error handling and validation is distributed across business logic, API controllers, data access layer in the form of conditions (“if-else” sequences). This leads to the violation of the Separation of Concerns Principle and results in “Spaghetti code”, like in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;....&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Annually&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// code 1&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Monthly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// code 2&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.....&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this article I describe the approach to splitting validation and error handling logic from the other application layers. The patterns and practices used below can be found in the Git Hub repository below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Boriszn/DeviceManager.Api" rel="noopener noreferrer"&gt;DeviceManager.Api&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture overview
&lt;/h2&gt;

&lt;p&gt;For simplicity I use N-tire architecture, however, explained approaches can be reused in CQRS, Event Driven, Micro Services, SOA, etc architectures.&lt;/p&gt;

&lt;p&gt;Example architecture includes following layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presentation Layer — UI / API&lt;/li&gt;
&lt;li&gt;Business Logic Layer — Services or Domain Services (in case you have DDD architecture)&lt;/li&gt;
&lt;li&gt;Data Layer / Data Access Layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the diagram below shows the components and modules which belong to different layers and contains presentation/API layer, business logic layer, data access, in the right side and related validation and error handling logic in the left side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AZbQ2SaW7CUPHAMS83DAxgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AZbQ2SaW7CUPHAMS83DAxgw.png" title="architecture" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The validation and error handling architecture contains several components which I will describe in next few sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  API validation level
&lt;/h2&gt;

&lt;p&gt;API controllers may contain a lot of validation such as parameters check, model state check etc like on example below. I will use declarative programming to move validation logic out from API controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SwaggerOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetDevices"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ValidateActionParameters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BadRequestObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDevices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;API controllers can be easily cleaned by creating validation model attribute. Example below contains simple model validation check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc.Filters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.ActionFilters&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Intriduces Model state auto validation to reduce code duplication&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;seealso cref="Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute" /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidateModelStateAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ActionFilterAttribute&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Validates Model automaticaly &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnActionExecuting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActionExecutingContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BadRequestObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just add this attribute to the startup.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMvc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;             
   &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValidateModelStateAttribute&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate parameters of API action methods I will create an attribute and move validation logic. Logic inside attribute checks if parameters contains validation attributes and validates the value.&lt;/p&gt;

&lt;p&gt;Now attribute can be added to the action method, if necessary. (examples below)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                               
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SwaggerOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetDevices"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;                               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ValidateActionParameters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                               
&lt;span class="p"&gt;{&lt;/span&gt;                                   
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDevices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;                               &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Business layer validation
&lt;/h2&gt;

&lt;p&gt;Business layer validation consists 2 components: validation service and validation rules.&lt;/p&gt;

&lt;p&gt;In the device validation services I’ve moved all custom validation and rule based validation logic from the service (Device service in the example below). This idea quite similar to using Guard pattern. Below the example of the validation service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Validation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FluentValidation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeviceValidationService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDeviceValidationService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IDeviceViewModelValidationRules&lt;/span&gt; &lt;span class="n"&gt;deviceViewModelValidationRules&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Initializes a new instance of the &amp;lt;see cref="DeviceValidationService"/&amp;gt; class.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="deviceViewModelValidationRules"&amp;gt;The device view model validation rules.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DeviceValidationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;IDeviceViewModelValidationRules&lt;/span&gt; &lt;span class="n"&gt;deviceViewModelValidationRules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deviceViewModelValidationRules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deviceViewModelValidationRules&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Validates the specified device view model.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="deviceViewModel"&amp;gt;The device view model.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;exception cref="ValidationException"&amp;gt;&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDeviceValidationService&lt;/span&gt; &lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceViewModel&lt;/span&gt; &lt;span class="n"&gt;deviceViewModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;validationResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deviceViewModelValidationRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceViewModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Validates the device identifier.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="deviceId"&amp;gt;The device identifier.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;exception cref="ValidationException"&amp;gt;Shuld not be empty&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDeviceValidationService&lt;/span&gt; &lt;span class="nf"&gt;ValidateDeviceId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should not be empty"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the rules I’ve moved all possible validation checks related to the view or API models. In the example below you can see Device view model validation rules. The validation itself triggers inside the the validation service.&lt;/p&gt;

&lt;p&gt;The Validation rules based on FluentValidation framework which allows you you build rules in fluent format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FluentValidation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Validation&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Validation rules related to Device controller&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeviceViewModelValidationRules&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeviceViewModelValidationRules&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Initializes a new instance of the &amp;lt;see cref="DeviceViewModelValidationRules"/&amp;gt; class.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;example&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// All validation rules can be found here: https://github.com/JeremySkinner/FluentValidation/wiki/a.-Index&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/example&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DeviceViewModelValidationRules&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeviceCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeviceCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exception handling Middleware
&lt;/h2&gt;

&lt;p&gt;The last turn I will cover errors/exception handling. I address this topic to the end as all validation components generate exceptions and the centralized component that handles them and provide proper JSON error object is required to have.&lt;/p&gt;

&lt;p&gt;In the example below I’ve used .NET core Middleware to catch all exceptions and created HTTP error status, according to Exception Type (in ConfigurationExceptionType method) and build error JSON object.&lt;/p&gt;

&lt;p&gt;Also Middleware can be used to log all exception in one place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FluentValidation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DeviceManager.Api.Middlewares&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Central error/exception handler Middleware&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExceptionHandlerMiddleware&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;JsonContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Initializes a new instance of the &amp;lt;see cref="ExceptionHandlerMiddleware"/&amp;gt; class.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="next"&amp;gt;The next.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ExceptionHandlerMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Invokes the specified context.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;The context.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ConfigurateExceptionTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// set http status code and content type&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpStatusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonContentType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// writes / returns error model to the response&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SerializeObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ErrorModelViewModel&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;
                    &lt;span class="p"&gt;}));&lt;/span&gt;

                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Configurates/maps exception to the proper HTTP error Type&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="exception"&amp;gt;The exception.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ConfigurateExceptionTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;httpStatusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Exception type To Http Status configuration &lt;/span&gt;
            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;httpStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                   &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;httpStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                  &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;httpStatusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In this article I covered several option to create maintainable validation architecture. The main goal of this article is to clean up business, presentation and data access logic. I would not recommend considering these approaches as “Silver bullets” as along with advantages they have several disadvantages.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Middleware — overrides existing response flow which good option for the API and may be disadvantage for Web solutions. You may need to have 2 middlewares for different solution types.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;All examples can be found implemented in the ready-to-go framework &lt;a href="https://github.com/Boriszn/DeviceManager.Api" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetcore</category>
      <category>validation</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building Multi-tenant Web API using dot net core and best practices (Tutorial)</title>
      <dc:creator>Boris Zaikin</dc:creator>
      <pubDate>Sun, 11 Nov 2018 15:18:06 +0000</pubDate>
      <link>https://dev.to/boriszn/building-multi-tenant-web-api-using-dot-net-core-and-best-practices-tutorial-4m8h</link>
      <guid>https://dev.to/boriszn/building-multi-tenant-web-api-using-dot-net-core-and-best-practices-tutorial-4m8h</guid>
      <description>&lt;p&gt;Business needs to grow in order to be successful and handle an increasing number of clients and partners, and if a company is not ready to respond to this load then there is a big chance that opportunities can be missed. This brings a topic of scalability into the game, as one of the main requirements that a company should address. As one of the possible ways to address this requirement is to build a multi-tenant solution. And as this topic gains more on importance, lots of options are available to achieve this, for example using Microsoft Elastic database (elastic tools). However in particular cases, like the case I faced on my project, not all of the product requirements could be satisfied with the already available options. This brought me to the idea of gathering my experience on this topic and presenting it below.&lt;/p&gt;

&lt;p&gt;As we all are aware there are two main approached to tackle applications scaling — horizontal and vertical. Horizontal scaling will bring you the benefit of scaling on the fly and will imply dealing with multiple databases as each tenant has its own database/shard. Vertical approach to scaling presumes having one database that serves several tenants.&lt;/p&gt;

&lt;p&gt;In my article I will address the approach of horizontal scaling with a step-by-step guide on how to build a multi-tenant web API application.&lt;/p&gt;

&lt;p&gt;If you would like to refresh some aspects of multi-tenant architecture or what are pros and cons it&lt;/p&gt;

&lt;p&gt;brings to the project, then I recommend visiting these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Cloud Architecture Matters: The Multi-Instance Advantage over Multi-Tenant&lt;/li&gt;
&lt;li&gt;Why Multi-Tenant Application Architecture Matters in 2017&lt;/li&gt;
&lt;li&gt;Design patterns for multi-tenant SaaS applications and Azure SQL Database&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Let’s briefly take a look at the architecture first. The example below is designed based on N-tire&lt;/p&gt;

&lt;p&gt;architecture and has the following layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presentation layer or web api&lt;/li&gt;
&lt;li&gt;Service layer that will accommodate all the business logic&lt;/li&gt;
&lt;li&gt;Data access layer that is implemented using UnitOfWork and Repository patterns. As ORM in this example I used Entity Framework Core.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key component of tenant separation is ContextFactory that contains logic to get the tenant id from HTTP header, retrieve a tenant database name using DataBaseManager and replace a database name in the connection string. As a result a database context (Entity Framework context) is created.&lt;/p&gt;

&lt;p&gt;The diagram below demonstrate this architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1000%2F1%2Ax3t-q0vFTY8L3Rn4J0pSAQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1000%2F1%2Ax3t-q0vFTY8L3Rn4J0pSAQ.png" title="architecture diagram" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementation&lt;/p&gt;

&lt;p&gt;As you can see architecture is not that complicated here, and skimming through it, I’d suggest to&lt;br&gt;
focus on the steps to implement it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create ContextFactory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned before ContextFactory is key component of whole architecture. It construct Entity Framework context (in current example DeviceApiContext) with specific to tenant database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Entity Framework context service&lt;/span&gt;
&lt;span class="c1"&gt;/// (Switches the db context according to tenant id field)&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;seealso cref="IContextFactory" /&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContextFactory&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IContextFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TenantIdFieldName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantid"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;DatabaseFieldKeyword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Database"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionSettings&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IDataBaseManager&lt;/span&gt; &lt;span class="n"&gt;dataBaseManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ContextFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;httpContentAccessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionSettings&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connectionSetting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IDataBaseManager&lt;/span&gt; &lt;span class="n"&gt;dataBaseManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpContentAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectionSetting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataBaseManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataBaseManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDbContext&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbOptionsBuidler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ChangeDatabaseNameInConnectionString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Add new (changed) database name to db options&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bbContextOptionsBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeleServiceApiContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;bbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbOptionsBuidler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DevicesApiContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Gets tenant id from HTTP header&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TenantId&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TenantIdFieldName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SqlConnectionStringBuilder&lt;/span&gt; &lt;span class="nf"&gt;ChangeDatabaseNameInConnectionString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sqlConnectionBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnectionStringBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultConnection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;dataBaseName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataBaseManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDataBaseName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBaseName&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBaseName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Remove old DataBase name from connection string AND add new one&lt;/span&gt;
        &lt;span class="n"&gt;sqlConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DatabaseFieldKeyword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;sqlConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DatabaseFieldKeyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataBaseName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sqlConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source code of ContextFactory available &lt;a href="https://github.com/Boriszn/DeviceManager.Api/blob/master/src/DeviceManager.Api/Data/Management/ContextFactory.cs" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add the data base manager&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Database manager orchestrates all tenants metadata such as tenant database name, activation&lt;/p&gt;

&lt;p&gt;status of tenants (activated/deactivated) and bunch of other properties. To demonstrate a base principle I used dictionary in the current solution. Later on dictionary should be replaced with more appropriate solutions, like SQL or NoSQL database that contains tenants metadata. This idea is similar to shard map manager that is used in Microsoft Elastic Tools. Also tenant metadata may includes fields to store database name, options to activate/deactivate tenants, even tenant styles for front-end application based on CSS/SASS/LESS file an on and on&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Contains all tenants database mappings and options&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataBaseManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDataBaseManager&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// IMPORTANT NOTICE: The solution uses simple dictionary for demo purposes.&lt;/span&gt;
    &lt;span class="c1"&gt;/// The Best "Real-life" solutions would be creating 'RootDataBase' with &lt;/span&gt;
    &lt;span class="c1"&gt;/// all Tenants Parameters/Options like: TenantName, DatabaseName, other configuration.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tenantConfigurationDictionary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"b0ed668d-7ef2-4a23-a333-94ad278f45d7"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"DeviceDb"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"e7e73238-662f-4da2-b3a5-89f4abb87969"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"DeviceDb-ten2"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets the name of the data base.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="tenantId"&amp;gt;The tenant identifier.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;db name&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetDataBaseName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataBaseName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenantConfigurationDictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBaseName&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBaseName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataBaseName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source code of &lt;code&gt;DataBaseManager&lt;/code&gt; available &lt;a href="https://github.com/Boriszn/DeviceManager.Api/blob/master/src/DeviceManager.Api/Data/Management/DataBaseManager.cs" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Add Unit of Work class (contains commit to specific context method)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;UnitOfWork solves two tasks. It commits all changes made by Entity Framework in entities and dispose specific context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// The Entity Framework implementation of UnitOfWork&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnitOfWork&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IUnitOfWork&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// The DbContext&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IDbContext&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Initializes a new instance of the &amp;lt;see cref="UnitOfWork"/&amp;gt; class.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;The object context&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UnitOfWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDbContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Saves all pending changes&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;The number of objects in an Added, Modified, or Deleted state&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Save changes with the default options&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc/&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDbContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Change context&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Save changes with the default options&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Disposes the current object&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SuppressFinalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Disposes all external resources.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="disposing"&amp;gt;The dispose indicator.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;disposing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disposing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source code of UnitOfWork available &lt;a href="https://github.com/Boriszn/DeviceManager.Api/blob/master/src/DeviceManager.Api/Data/Management/UnitOfWork.cs" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Add Generic Repository class.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Repository will make changes in EF entities and Unit of Work will commit changes to tenants database. Be aware that EF making changes in memory, using &lt;a href="https://docs.microsoft.com/en-us/ef/core/querying/tracking" rel="noopener noreferrer"&gt;Tracking Mechanism.&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Generic repository, contains CRUD operation of EF entity&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;Entity type&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Used to query and save instances of&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets the EF context.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;value&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// The context.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDbContext&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Initializes a new instance of the &amp;lt;see cref="Repository{T}" /&amp;gt; class.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="contextFactory"&amp;gt;The context service.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IContextFactory&lt;/span&gt; &lt;span class="n"&gt;contextFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contextFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DbContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TKey&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TKey&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;params&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;keyValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyValues&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FindBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FindBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dbSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source code of Repository available &lt;a href="https://github.com/Boriszn/DeviceManager.Api/blob/master/src/DeviceManager.Api/Data/Management/Repository.cs" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Add tenant header operation filter.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TenantHeaderOperationFilter class will add tenant id field to all API calls (as HTTP header). In solutions which uses OIDS services e.g IdentityServer or auth0.com tenant can be injected to JWT token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Adds Tenant Id field to API endpoints&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;seealso cref="Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter" /&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TenantHeaderOperationFilter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOperationFilter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Applies the specified operation.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="operation"&amp;gt;The operation.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;The context.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Operation&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OperationFilterContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IParameter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;NonBodyParameter&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Required&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how API will look like after filter applied.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1000%2F1%2A2Lw0-R7Zn-_s7rzDcIejkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1000%2F1%2A2Lw0-R7Zn-_s7rzDcIejkg.png" title="swagger api demo" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Current example of service class (DeviceService.cs) contains functions to retrieving device by id and add new device for specific tenants. Source Code of service layer available &lt;a href="https://github.com/Boriszn/DeviceManager.Api/blob/master/src/DeviceManager.Api/Services/DeviceService.cs" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;In this article I was explained how to build “ready to go” Multi tenant solution and given some suggestions how it can be used in your product/business. As I mentioned before this solution is ready to go so it can be used as “Boilerplate” project or partially.&lt;/p&gt;

&lt;p&gt;Source code&lt;/p&gt;

&lt;p&gt;The project’s source code available in Git repository &lt;a href="https://github.com/Boriszn/DeviceManager.Api" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>multitenantcy</category>
      <category>api</category>
      <category>webapi</category>
      <category>dotnetcore</category>
    </item>
  </channel>
</rss>
