<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://aztoso.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://aztoso.com/" rel="alternate" type="text/html" /><updated>2023-01-12T13:23:57+01:00</updated><id>https://aztoso.com/feed.xml</id><title type="html">AzToso.com</title><subtitle>Azure experience from the field translated into blog posts</subtitle><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><entry><title type="html">Managed Identities vs Service Principals - when to use what ?</title><link href="https://aztoso.com/security/managed-identities-vs-service-principals/" rel="alternate" type="text/html" title="Managed Identities vs Service Principals - when to use what ?" /><published>2023-01-12T01:00:00+01:00</published><updated>2023-01-12T01:00:00+01:00</updated><id>https://aztoso.com/security/managed-identities-vs-service-principals</id><content type="html" xml:base="https://aztoso.com/security/managed-identities-vs-service-principals/">&lt;p&gt;On this page, you can find an overview of the identity types available in Azure and a recommendation on when to use what. At the end, there is a section with an example implementation of the best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway&lt;/strong&gt;: Always preffer Managed Identities over service principals.&lt;/p&gt;

&lt;h1 id=&quot;identity-types&quot;&gt;Identity types&lt;/h1&gt;
&lt;h2 id=&quot;managed-identities&quot;&gt;Managed Identities&lt;/h2&gt;

&lt;p&gt;Managed Identities eliminate the need for users to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens.&lt;/p&gt;

&lt;p&gt;Top 3 benefits of using Managed Identities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Eliminate the need for users to manage credentials&lt;/li&gt;
  &lt;li&gt;Nonexportable certificate-based credentials, valid for 90 days, rolled after 45 days&lt;/li&gt;
  &lt;li&gt;Uses long-lived tokens (24 hours) with a proactive token refresh every 12 hours (it can handle a maximum Azure AD downtime between 12-24 hours)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Managed Identity is an Azure Resource Manager (ARM) object you create in a resource group. During the creation, you specify the location for the Managed Identity, which is just for storing the metadata. Managed Identities are special service principals created in Azure Active Directory (AAD). The Managed Identities will continue to work if there is a region outage. You can use Managed Identities only with Azure Resources to access other Azure Resources (VM access to KeyVault, for example), or any other OAuth2 protected endpoint.&lt;/p&gt;

&lt;p&gt;You can create two types of Managed Identities:&lt;/p&gt;

&lt;h3 id=&quot;system-assigned-managed-identities&quot;&gt;System-assigned Managed Identities&lt;/h3&gt;
&lt;p&gt;The Managed Identity is tied to the lifecycle of the resources, which means that if you delete the resource, the Managed Identity will also be deleted. For that reason, it is not visible in the portal as a separate resource.&lt;/p&gt;

&lt;p&gt;Use System-assigned Managed Identities when:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you want to be 100% sure that only that and no other resource can access a particular target&lt;/li&gt;
  &lt;li&gt;you don’t need to pre-provision the access before you create the Azure resource with enabled System-assigned Managed Identity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;user-assigned-managed-identities&quot;&gt;User-assigned Managed Identities&lt;/h3&gt;
&lt;p&gt;The Managed Identity is created as a separate resource in Azure.&lt;/p&gt;

&lt;p&gt;Use User-assigned Managed Identities when:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you want to assign the same identity to multiple Azure resources&lt;/li&gt;
  &lt;li&gt;you need to pre-provision the access before you create the Azure resource&lt;/li&gt;
  &lt;li&gt;the principal you use for the deployment of your application doesn’t have an owner or user access administrator rights (which is a best practice)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a best practice, use a separate deployment stack to create User-assigned Managed Identities in a dedicated resource group. Also, ensure you fine-grain the managed identity permissions and don’t use a single Manage Identity for everything you deploy. A good practice is to have a single identity per workload. The minimal RBAC role to be able to assign them to an Azure resource is &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#managed-identity-operator&quot;&gt;Managed Identity Operator&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;service-principals&quot;&gt;Service principals&lt;/h2&gt;
&lt;p&gt;Although Managed Identity is a special type of service principal, we usually refer to a service principal as a local representation, or application instance, of a global application object in a single tenant or directory. A service principal object is created when an application is given permission to access resources in a tenant (upon registration or consent). A service principal is created automatically when you register an application using the Azure portal. You can also create service principal objects in a tenant using Azure PowerShell, Azure CLI, Microsoft Graph, and other tools. A good overview of the differences between application ↔ service principal is given on the &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals&quot;&gt;following page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In some scenarios you must use service principals because Managed Identities are not supported. Keep in mind that you need to regulary rotate the secrets. One good practice is to create a new secret with each deployment as a step in your release pipeline. Check the example GitHub action bellow that can help you achiving this.&lt;/p&gt;

&lt;h1 id=&quot;when-to-use-what&quot;&gt;When to use what?&lt;/h1&gt;
&lt;h2 id=&quot;github-actions&quot;&gt;GitHub Actions&lt;/h2&gt;
&lt;p&gt;At the end of 2021, GitHub added OpenID Connect (OIDC) support to GitHub Actions. OIDC allows you to use &lt;a href=&quot;https://docs.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-beta&quot;&gt;Federated Credentials&lt;/a&gt; for your service principal, &lt;strong&gt;without requiring a secret&lt;/strong&gt; and its subsequent rotation. Detailed steps to set this up are in the &lt;a href=&quot;https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#use-the-azure-login-action-with-openid-connect&quot;&gt;Azure&lt;/a&gt; documentation. With the federated identity in place, you no longer need to worry about secret rotations.&lt;/p&gt;

&lt;h2 id=&quot;azure-devops&quot;&gt;Azure DevOps&lt;/h2&gt;
&lt;p&gt;From Azure DevOps, you can connect to Azure using Azure Resource Manager service connections. There are several types of connections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service principal (automatic): Azure DevOps automatically create a service principal with a valid secret for &lt;strong&gt;two years&lt;/strong&gt;. To rotate the secret, you must edit the service connection and click Verify.&lt;/li&gt;
  &lt;li&gt;Service principal (manual): you manually create the service principal and assign it to the service connection. To rotate the secret, you need to generate a new secret in Azure AD and update the service connection.&lt;/li&gt;
  &lt;li&gt;Managed identity: you need to run your own Azure DevOps agents in Azure VMs with assigned managed identities. For this service connection, you don’t need to rotate the secrets.&lt;/li&gt;
  &lt;li&gt;Publish profile: it uses a publish profile to deploy to a WebApp&lt;/li&gt;
  &lt;li&gt;“Implicit”: this creates a service principal behind the scenes when choosing service connection types that connect to an Azure subscription. Examples are:
    &lt;ul&gt;
      &lt;li&gt;Docker Registry (when choosing the Azure Container Registry subtype)&lt;/li&gt;
      &lt;li&gt;Kubernetes (when choosing the Azure Kubernetes Service subtype)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a general best practice, deploy a self-hosting agents in Azure and use Managed Identities.&lt;/p&gt;

&lt;h2 id=&quot;virtual-machinesvirtual-machine-scale-sets&quot;&gt;Virtual Machines/Virtual Machine Scale Sets&lt;/h2&gt;
&lt;p&gt;If you need to access another Azure service (including databases) from within the Azure VM, you can use Managed Identities almost in all scenarios. The same applies if you need to access any OAuth2 endpoint (for example, an application or service).&lt;/p&gt;

&lt;p&gt;Under the hood, you will use the Azure Instance Metadata Service to get the OAuth2 token for the assigned Managed Identity and present that token to the endpoint you call. Check the &lt;a href=&quot;https://learn.microsoft.com/en-gb/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token&quot;&gt;Microsoft documentation&lt;/a&gt; on how that works and how you can grab the token using different programming languages.&lt;/p&gt;

&lt;h2 id=&quot;azure-paas-services&quot;&gt;Azure PaaS services&lt;/h2&gt;
&lt;p&gt;Almost all of the Azure PaaS Services support Managed Identities. As a general best practice, always use them when accessing any other OAuth2 endpoint, including other Azure services.&lt;/p&gt;

&lt;p&gt;For example, in Logic Apps, use the Managed Identity to access a storage account or a Log Analytics workspace. If that is not possible with the built-in connector, check the Rest APIs because they usually support the OAuth2 authorization workflow.&lt;/p&gt;

&lt;h2 id=&quot;promitor&quot;&gt;Promitor&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.promitor.io/v2.8/&quot;&gt;Promitor&lt;/a&gt; is an Azure Monitor scraper that makes the metrics available for Prometheus. When configuring, make sure you &lt;a href=&quot;https://docs.promitor.io/v2.8/scraping/runtime-configuration/#authentication&quot;&gt;use a Managed Identity to connect to Azure Monitor&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;grafana&quot;&gt;Grafana&lt;/h2&gt;
&lt;p&gt;Grafana also supports a Managed Identity when connecting to an Azure Monitor data source. To make use, you need to &lt;a href=&quot;https://grafana.com/docs/grafana/v8.5/datasources/azuremonitor/#configuring-using-managed-identity&quot;&gt;enable the feature in the configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you use Azure Active Directory as an identity provider for Grafana, you must use a service principal and add its secret to the Grafana configuration file. This means that you must rotate that secret. As mentioned above, you create a new secret with each deployment as a step in your release pipeline and redeploy Grafana every 3 months (for example). Check the example GitHub action bellow that can help you achiving this.&lt;/p&gt;

&lt;h2 id=&quot;azure-kubernetes-service-aks&quot;&gt;Azure Kubernetes Service (AKS)&lt;/h2&gt;
&lt;h3 id=&quot;cluster--kubelet-identity&quot;&gt;Cluster &amp;amp; Kubelet Identity&lt;/h3&gt;
&lt;p&gt;AKS needs its identity to create additional resources like load balancers and managed disks or fetch container images from an Azure Container Registry. Always use a Managed Identity for both cluster and kubelet identities. If you have a running cluster that uses service principals, you can convert it to Managed Identity by following &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/aks/use-managed-identity#update-an-aks-cluster-to-use-a-managed-identity&quot;&gt;this procedure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you often redeploy the clusters (blue/green deployments) to an existing VNET, using User Assigned Managed Identities for both cluster and kublet makes more sense because you can use the same identity for all your deployments without the need to pre-provision the permissions with each deployment:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az aks create &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; MyResourceGroup &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; MyManagedCluster &lt;span class=&quot;nt&quot;&gt;--assign-identity&lt;/span&gt; &amp;lt;control-plane-identity-resource-id&amp;gt; &lt;span class=&quot;nt&quot;&gt;--assign-kubelet-identity&lt;/span&gt; &amp;lt;kubelet-identity-resource-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;pod-identities&quot;&gt;Pod identities&lt;/h3&gt;
&lt;p&gt;As a best practice, you should not use fixed credentials within pods or container images, as they are at risk of exposure or abuse. Instead, assign a managed identity to pods to automatically request access to resources using a central Azure AD identity solution. &lt;a href=&quot;https://aztoso.com/aks/baseline-part-4/&quot;&gt;AAD Pod Identity&lt;/a&gt; enables Kubernetes applications to access cloud resources securely with Azure Active Directory. Using Kubernetes primitives, administrators configure identities and bindings to match pods. Then without any code modifications, your containerized applications can leverage any resource in the cloud that supports AAD as an identity provider.&lt;/p&gt;

&lt;p&gt;Always use user-managed identities (type 0) when you create AzureIdentity definitions. Make sure those identities follow the lifecycle of the applications or the AKS cluster. Using service principals is strongly not recommended.&lt;/p&gt;

&lt;h3 id=&quot;azure-ad-workflow-identities&quot;&gt;Azure AD Workflow identities&lt;/h3&gt;
&lt;p&gt;AAD workflow identity integrates with the Kubernetes native capabilities to federate with external identity providers. It is the next big thing for handling identities in AKS. In a nutshell, it enables you to exchange the Kubernetes service account token for an OAuth2 token representing a Managed Identity. More details about how it works are available in the &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;kafka-event-hubs&quot;&gt;Kafka (Event Hubs)&lt;/h2&gt;
&lt;p&gt;Use a Managed Identity to connect to an OAuth2 protected listener in Kafka. (This repo)[https://github.com/Azure/azure-event-hubs-for-kafka/tree/master/tutorials/oauth/java/managedidentity] provides an example of how you can build consumer and producer applications in java that connects to an Azure Event Hub using the Kafka protocol.&lt;/p&gt;

&lt;h1 id=&quot;best-practices-in-action&quot;&gt;Best practices in action&lt;/h1&gt;
&lt;h2 id=&quot;access-hashicorp-vault-using-managed-identities&quot;&gt;Access HashiCorp Vault using Managed Identities&lt;/h2&gt;
&lt;p&gt;Enable Azure Auth on your HashiCorp namespace and allow access from the managed identity by assigning the policy. It is highly recommended  to use User Assigned Managed Identity because they can reuse and assign the same identity to multiple resources:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Login TO THE VAULT&lt;/span&gt;
vault login &lt;span class=&quot;nt&quot;&gt;-method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;oidc &lt;span class=&quot;c&quot;&gt;# make sure you login as a namespace admin&lt;/span&gt;
 
&lt;span class=&quot;c&quot;&gt;# Enable Azure Auth&lt;/span&gt;
vault write auth/azure/config &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;tenant_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;YOUR_AAD_TENANT_ID&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://management.azure.com
 
&lt;span class=&quot;c&quot;&gt;# Enable a KV-V2 secret engine to store an example secret at a custom path&lt;/span&gt;
vault secrets &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;example-secrets kv-v2
 
&lt;span class=&quot;c&quot;&gt;# Add an example secre into the vault&lt;/span&gt;
vault kv put example-secrets/example-password &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;password1234
 
&lt;span class=&quot;c&quot;&gt;# Create an example policy&lt;/span&gt;
vault policy write example-policy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    path &lt;span class=&quot;s2&quot;&gt;&quot;example-secrets/data/example-password&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    capabilities &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;read&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;c&quot;&gt;# Create a role for the Managed Identity. IDENTITY_PRINCIPAL_ID = the object (principal) id of the Managed Identity&lt;/span&gt;
 vault write auth/azure/role/example-role &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;example-policy&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;bound_service_principal_ids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IDENTITY_PRINCIPAL_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;get-secrets-from-azure-key-vault-with-ansible-using-managed-identities&quot;&gt;Get secrets from Azure Key Vault with Ansible using Managed Identities&lt;/h2&gt;
&lt;p&gt;Using Managed Identity, you can use the &lt;a href=&quot;https://github.com/ansible-collections/azure/blob/dev/plugins/lookup/azure_keyvault_secret.py&quot;&gt;azure_keyvault_secret lookup&lt;/a&gt; to get a secret from an Azure Key Vault. A sample implementation is provided by the CIT-DSP team here.&lt;/p&gt;

&lt;h2 id=&quot;github-actions-sample-use-cases&quot;&gt;GitHub Actions sample use cases&lt;/h2&gt;
&lt;h3 id=&quot;upload-files-to-a-storage-account-with-federated-identity-no-secretstorage-access-key&quot;&gt;Upload files to a storage account with federated identity (no secret/storage access key)&lt;/h3&gt;
&lt;p&gt;Ensure that the service principal you use in the azure/login action has at least the &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-blob-data-contributor&quot;&gt;Storage Blob Data Contributor&lt;/a&gt; Azure RBAC role on the storage container.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name: Upload file to a storage account
on: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;push]
 
permissions:
      id-token: write
      contents: &lt;span class=&quot;nb&quot;&gt;read
       
jobs&lt;/span&gt;:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - name: &lt;span class=&quot;s1&quot;&gt;'check the code'&lt;/span&gt;
      uses: actions/checkout@v2
    - name: &lt;span class=&quot;s1&quot;&gt;'Az CLI login'&lt;/span&gt;
      uses: azure/login@v1
      with:
          client-id: &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;
          tenant-id: &lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;        
          allow-no-subscriptions: &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
   
    - name: &lt;span class=&quot;s1&quot;&gt;'Run Azure CLI commands'&lt;/span&gt;
      run: |        
         az storage blob upload-batch &lt;span class=&quot;nt&quot;&gt;--auth-mode&lt;/span&gt; login &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://tttosokrtest.blob.core.windows.net/upload'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;rotate-service-principals-secrets-with-federated-identity&quot;&gt;Rotate service principal’s secrets with federated identity&lt;/h3&gt;
&lt;p&gt;Check the &lt;a href=&quot;https://github.com/marketplace/actions/rotate-azure-active-directory-service-principal-secret&quot;&gt;GitHub action&lt;/a&gt; I build for this purpose.&lt;/p&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Security" /><category term="Managed Identity" /><summary type="html">Managed Identities eliminate the need for users to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens. In scenarios when Managed Identies are not supported, you must use service principals. When to use what?</summary></entry><entry><title type="html">How to get the System Assigned Managed Identity Object Id from within the VM?</title><link href="https://aztoso.com/security/system-assigned-managed-identity-vm-objectid-terraform/" rel="alternate" type="text/html" title="How to get the System Assigned Managed Identity Object Id from within the VM?" /><published>2022-10-13T02:00:00+02:00</published><updated>2022-10-13T02:00:00+02:00</updated><id>https://aztoso.com/security/terraform-managed-identity-object-id</id><content type="html" xml:base="https://aztoso.com/security/system-assigned-managed-identity-vm-objectid-terraform/">&lt;p&gt;While working with Terraform and Azure DevOps self-hosted agent, I faced a challenge getting the Object(Principal) Id of a System Assigned Managed Identity to the VM running the agent. I needed that Object Id to assign access to a Key Vault. There is a way to do so via Azure CLI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az resource list &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; Metadata:true &lt;span class=&quot;nt&quot;&gt;--noproxy&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*'&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'http://169.254.169.254/metadata/instance?api-version=2021-02-01'&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; .compute.name&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; Metadata:true &lt;span class=&quot;nt&quot;&gt;--noproxy&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*'&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'http://169.254.169.254/metadata/instance?api-version=2021-02-01'&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; .compute.resourceGroupName&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{principal_id:[0].identity.principalId,tenant_id:[0].identity.tenantId}'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--out&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;but that approach requires the installation of Azure CLI and configuring Reader access on the VM itself.&lt;/p&gt;

&lt;p&gt;A more elegant approach is to extract the Object Id from the access token received by &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token&quot;&gt;calling the Azure Instance Metadata Service (IMDS) endpoint&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&amp;amp;resource=https://management.azure.com/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Metadata: true&quot;&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
access_token | jq &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'split(&quot;.&quot;) | .[1] | @base64d | fromjson | .oid'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In Terraform, you can use the external provider to execute the script:&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;external&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;account_info&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&amp;amp;resource=https://management.azure.com/' --header 'Metadata: true' | jq -r .access_token | jq -R 'split(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) | .[1] | @base64d | fromjson | {oid: .oid, appid: .appid,tid: .tid}'&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then access the Object Id (and the Tenant Id) with:&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;tomap&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;account_info&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_key_vault_access_policy&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;current-user&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_vault_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tomap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;account_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tid&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;object_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tomap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;account_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oid&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;key_permissions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Purge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Recover&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Update&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;List&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Decrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Sign&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Security" /><category term="Managed Identity" /><category term="Terraform" /><summary type="html">To get the Object Id of the VM's System Assigned Managed Identity, you need to call the Azure Instance Metadata Service (IMDS) endpoint and use the provided attributes to get the required information. It is more challenging when you want to achieve the same thing with Terraform.</summary></entry><entry><title type="html">Deep dive into Azure Container Apps - Overview and Networking</title><link href="https://aztoso.com/container-apps/deep-dive-overview-and-networking/" rel="alternate" type="text/html" title="Deep dive into Azure Container Apps - Overview and Networking" /><published>2022-08-22T02:00:00+02:00</published><updated>2022-08-22T02:00:00+02:00</updated><id>https://aztoso.com/container-apps/container-apps-overview-and-networking</id><content type="html" xml:base="https://aztoso.com/container-apps/deep-dive-overview-and-networking/">&lt;p&gt;&lt;a href=&quot;/container-apps/deep-dive-operating-security-reliability-pricing/&quot;&gt;Deep dive into Azure Container Apps - Operating, security, reliability and pricing&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/overview&quot;&gt;Azure Container Apps&lt;/a&gt; is a serverless offering you can use to host your containers. It is a good fit for containerized apps and hosting microservices. Integrated services like KEDA, Envoy proxy, and Dapr provide you with out-of-the-box auto-scaling, ingress, traffic splitting, and simplified microservice connectivity.&lt;/p&gt;

&lt;p&gt;Container Apps service is built on top of Kubernetes. Container Apps are an Azure Resource Manager deployment object, meaning you can’t just use your existing Kubernetes object descriptions and migrate them to Container Apps. You need to rewrite your deployment stack using Bicep or ARM templates. Terraform is not yet supported.&lt;/p&gt;

&lt;p&gt;For each Container App, you need to specify the resources you want to allocate. The current range starts from 0.25 vCPU and 0.5Gi memory up to 2 vCPUs and 4.0Gi memory. If your container needs more resources to operate, you must brake your business logic down into multiple containers (or refer to them as microservices). Container Apps is not a good fit for you if that is not possible.&lt;/p&gt;

&lt;p&gt;This document provides deep dive into Container Apps by clarifying not well-documented features, provides recommendations for deployments and gives examples of how to perform some of the actions. For a complete reference, check the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/&quot;&gt;official product page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;when-are-container-apps-a-good-fit&quot;&gt;When are Container Apps a good fit?&lt;/h2&gt;
&lt;p&gt;If you want to avoid the complexity of operating a Kubernetes cluster, Container Apps are a good fit. It means that you need to trade off the control you have in Kubernetes for the simplicity the Container Apps service brings. For example, with Kubernetes, you are free to add platform features like automatic certificate rotations, external DNS registration, etc. In Container Apps, you need to wait for Microsoft to introduce such features in the platform.&lt;/p&gt;

&lt;p&gt;If you want to host a website, App Service is a much better choice for that. If the website is a microservice application composed of multiple containers, Container Apps is a better fit.&lt;/p&gt;

&lt;p&gt;Azure Container Instances is the preferred option for running single isolated containers that perform background processing and don’t need to scale out.&lt;/p&gt;

&lt;h2 id=&quot;dapr&quot;&gt;Dapr&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://dapr.io/&quot;&gt;Dapr (Distributed Application Runtime)&lt;/a&gt; is a portable, serverless, event-driven runtime that makes it easy for developers to build resilient, stateless, and stateful microservices that run on the cloud and edge and embraces the diversity of languages and developer frameworks. Dapr building blocks expose common distributed application capabilities, such as state management, service-to-service invocation, and pub/sub messaging&lt;/p&gt;

&lt;p&gt;If you enable Dapr in Container Apps, it will inject a sidecar container which you can use for the microservices connectivity. You enable Dapr per Container App, meaning that in a Container Apps Environment, you can have apps with and without Dapr support. After the sidecar injection, you can talk to the Dapr container via HTTP or gRPC. Calls between Dapr sidecars are always made with gRPC, which delivers high performance. Dapr is cloud-agnostic and provides language-specific SDKs for easy development. The benefit of Dapr is that it speeds up application development by eliminating the need for a plumbing code while simultaneously, built applications are platform (cloud) agnostic.&lt;/p&gt;

&lt;h1 id=&quot;networking&quot;&gt;Networking&lt;/h1&gt;
&lt;h2 id=&quot;ingress-controller&quot;&gt;Ingress controller&lt;/h2&gt;
&lt;p&gt;An Envoy proxy is used on the backend to provide ingress connectivity to the Container Apps. As part of the Container Apps deployment, you can configure the accessibility level (public, VNET, or Container Apps Environment), traffic split rules (when using multiple revisions), custom DNS name, and certificate to associate with the ingress. Other configuration options for the ingress are not available. Because the ingress is part of the platform, you don’t need to take care of updating it.&lt;/p&gt;

&lt;p&gt;By default, ports 80 and 443 are open with HTTP to HTTPS redirection. As a general security guideline, ALWAYS enable only HTTPS traffic, even when you allow connection only from Container Apps Environment.&lt;/p&gt;

&lt;p&gt;The fully qualified domain name (FQDN) associated with the ingress depends on the accessibility level:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Ingress accessibility&lt;/th&gt;
      &lt;th&gt;FQDN&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Public&lt;/td&gt;
      &lt;td&gt;APP_NAME.UNIQUE_IDENTIFIER.REGION_NAME.azurecontainerapps.io&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;VNET only&lt;/td&gt;
      &lt;td&gt;APP_NAME.UNIQUE_IDENTIFIER.REGION_NAME.azurecontainerapps.io&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Container Apps Environment only&lt;/td&gt;
      &lt;td&gt;APP_NAME.internal.UNIQUE_IDENTIFIER.REGION_NAME.azurecontainerapps.io&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; mTLS is not (yet) supported by the ingress controller.  Use OAuth2 to secure your application.&lt;/p&gt;

&lt;h2 id=&quot;custom-dns&quot;&gt;Custom DNS&lt;/h2&gt;
&lt;p&gt;Azure Container Apps allows you to bind one or more custom domains to a container app. For each custom domain, you also need a Server Name Indication (SNI) domain certificate associated with the ingress.&lt;/p&gt;

&lt;p&gt;Steps to follow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Upload a private key certificate (.pfx or .pem) to the Container Apps Environment.&lt;/li&gt;
  &lt;li&gt;In Container Apps, link the certificate with the custom DNS domain name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no possibility of storing the certificate in a Key Vault. By developing an automation script, you can rotate the certificate on a scheduled basis.&lt;/p&gt;

&lt;h2 id=&quot;communication-between-container-apps&quot;&gt;Communication between Container Apps&lt;/h2&gt;
&lt;p&gt;If you enable ingress,  your Container App will be exposed using a dedicated DNS name. You can use that name to reach the Container App from other Container Apps. It is worth mentioning that the communication between Container Apps deployed in the same environment never leaves the environment:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/communication.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the picture, the environment DNS suffix is available inside containers via the environment variable CONTAINER_APP_ENV_DNS_SUFFIX.  You can append that value to the name of the Container App you want to establish a connection to.&lt;/p&gt;

&lt;p&gt;If you enable Dapr, you can levarage the built-in service discovery (the command is executed from another container app, using the console):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/communication-dapr.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;vnet-integration&quot;&gt;VNET integration&lt;/h2&gt;
&lt;p&gt;By default, when you create Container Apps Environment, it uses a managed VNET, that is a VNET that is part of the service, and you can’t see it or perform any configurational changes. If you want to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use private endpoints (Private Link) to connect to your service (for example, database )&lt;/li&gt;
  &lt;li&gt;establish a private connection to a service that offers a VNET integration (such as Flexible Server for PostgreSQL)&lt;/li&gt;
  &lt;li&gt;communicate to a service that is available only on your internal network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then you need to use a custom VNET when creating the Container Apps Environment.&lt;/p&gt;

&lt;p&gt;With custom VNET, you need a minimum of /23 subnet dedicated to Container Apps Environment. When deployed, the service reserves 60 IPs per VMSS configuration, meaning that if you enable Availability Zone, it will reserve 3 x 60 = 180 IPs.&lt;/p&gt;

&lt;h3 id=&quot;custom-vnet-with-an-internal-environment&quot;&gt;Custom VNET with an internal environment&lt;/h3&gt;
&lt;p&gt;When using an internal environment (without a public exposure), the underneath Kubernetes cluster is exposed using a Private Load Balancer, which consumes a single IP address from the subnet where it is deployed. In the managed resource group (starts with mc_) there is also a Public Load Balancer deployed, which is used only for outbound connections, providing Internet connectivity to underlying Kubernetes nodes and deployed revisions.&lt;/p&gt;

&lt;p&gt;To resolve the DNS names of your Container Apps from the VNET, you need to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a Private DNS Zone using the Container Apps Environment default domain name. The default domain is in the format: VARIABLE_NAME.LOCATION.azurecontainerapps.io. For example icyplant-362dc571.westeurope.azurecontainerapps.io&lt;/li&gt;
  &lt;li&gt;Create a “star” DNS record inside the zone, pointing to the Internal Load Balancer IP address.&lt;/li&gt;
  &lt;li&gt;Link the Private DNS Zone with your VNET&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/internal_evironment_with_custom_vnet.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It is impossible to use a custom private DNS zone for the Container Apps, because the service can’t verify the ownership. You have two options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use a public DNS zone to create a private IP record&lt;/li&gt;
  &lt;li&gt;create a link to container apps environment Private DNS zone to all VNETs that need access to your service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you choose to enable the HTTP ingress, you can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;limit the traffic to the VNET - everyone who has network connectivity to your VNET will potentially have access&lt;/li&gt;
  &lt;li&gt;limit to the Container Apps Environment - the app will be reachable only within the environment by other Container Apps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;custom-vnet-with-an-external-environment&quot;&gt;Custom VNET with an external environment&lt;/h3&gt;
&lt;p&gt;With an external environment, the configuration is more straightforward because there is no need for a private DNS zone integration into the VNET.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/external_evironment_with_custom_vnet.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you choose to enable the HTTP ingress, you can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;limit to the Container Apps Environment - the app will be reachable only within the environment by other Container Apps&lt;/li&gt;
  &lt;li&gt;accept traffic from everywhere - everyone who has network connectivity to your VNET will potentially have access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose you want to apply an allow list to the incoming connections. In that case, you must create a Network Security Group (NSG) and associate it to the subnet where the Container Apps are deployed. The rule should look like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Source: List of source IP addresses&lt;/li&gt;
  &lt;li&gt;Source port ranges: * (usually allow all source ports)&lt;/li&gt;
  &lt;li&gt;Destination: The public IP address of the environment. &lt;img src=&quot;/assets/img/posts/container-apps/environment_ip.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Destination port ranges: 443&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never modify the NSG (aks-agentpool-{id}-nsg) that is deployed in the mc_ managed resource group. The Container Apps service controls that NSG, and it can be overwritten.&lt;/p&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Container Apps" /><category term="Kubernetes" /><category term="ContainenerApps" /><summary type="html">Azure Container Apps is a serverless offering you can use to host your containers. It is a good fit for containerized apps and hosting microservices. Integrated services like KEDA, Envoy proxy, and Dapr provide you with out-of-the-box auto-scaling, ingress, traffic splitting, and simplified microservice connectivity. Container Apps service is built on top of Kubernetes.</summary></entry><entry><title type="html">Deep dive into Azure Container Apps - Operating, security, reliability and pricing</title><link href="https://aztoso.com/container-apps/deep-dive-operating-security-reliability-pricing/" rel="alternate" type="text/html" title="Deep dive into Azure Container Apps - Operating, security, reliability and pricing" /><published>2022-08-22T02:00:00+02:00</published><updated>2022-08-22T02:00:00+02:00</updated><id>https://aztoso.com/container-apps/container-apps-operating-security-reliability-pricing</id><content type="html" xml:base="https://aztoso.com/container-apps/deep-dive-operating-security-reliability-pricing/">&lt;p&gt;&lt;a href=&quot;/container-apps/deep-dive-overview-and-networking/&quot;&gt;Deep dive into Azure Container Apps - Overview and Networking&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;operating-container-apps&quot;&gt;Operating Container Apps&lt;/h1&gt;
&lt;h2 id=&quot;revisions&quot;&gt;Revisions&lt;/h2&gt;
&lt;p&gt;In short, revisions are the versions of the Container App. A new revision is created when a revision-scope change is applied to the Container App, such as a change in the configuration or an image version. A Container App can have single or multiple active revisions at the same time. Multiple revisions enable traffic splitting, where a predefined percentage of the traffic can hit particular revisions, which is helpful in A/B testing and Blue/Green deployments scenarios.&lt;/p&gt;

&lt;p&gt;By default, the revision name is composed by applying an alphanumerical random suffix to the Container App Name, like CONTAINER_APP_NAME–RANDOM_SUFFIX.  It is recommended to replace the random with a custom suffix by using the commit id of your deployment repo (populate the value dynamically during the deployment).The commit id is helping in the case when you need to apply revision-scope changes without changing the image version. The release tag is not a good candidate because it usually contains dots that are not a good accepted in the revision suffix.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/revisions#revision-labels&quot;&gt;revision labels&lt;/a&gt; can help you directly reach the desired revision if you plan to use traffic splitting. This can be useful if you want to perform some smoke test before gradually switching the traffic to the latest version (revision) or perform a full switch when using blue/green deployments. With that in mind, having the “latest” revision label applied to the last revision makes sense. You can move the label between the revisions as desired.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;traffic: [         
    {           
        revisionName: '${containerAppName}--${previousRevisionSuffix}'
        weight: 100
    }
    {
        revisionName: '${containerAppName}--${newRevisionSuffix}'
        label: 'latest'
        weight: 0
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When using the multiple revision mode, revisions remain active until you disable them. If your revision doesn’t scale to zero, you will pay for each active revision. As a best practice, always disable the revisions you don’t need.&lt;/p&gt;

&lt;h2 id=&quot;auto-scaling&quot;&gt;Auto-scaling&lt;/h2&gt;
&lt;p&gt;Container Apps support three scale triggers: HTTP traffic, Event-driven, and Utilization (CPU or Memory usage). The utilization trigger doesn’t support scale to zero. If you plan to use the KEDA event-based triggers, it is recommended to set the revision mode to single (activeRevisionMode: ‘single’). The default settings (it is not clear if those are possible to change) of the auto scaler are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;pooling interval (scale-out): 30 seconds&lt;/li&gt;
  &lt;li&gt;cool down interval (scale-in): 300 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;health-probes&quot;&gt;Health probes&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/health-probes?tabs=arm-template&quot;&gt;Service supports&lt;/a&gt; liveness, readiness and startup probes which are actually based on the standard Kubernetes health probes. You have an option to use HTTP or TCP for the probes. Make sure that your Container Apps always use health probes, which will lead to an increased overall availability of your applocations.&lt;/p&gt;

&lt;h2 id=&quot;storage&quot;&gt;Storage&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Volume mounting is in preview&lt;/p&gt;

&lt;p&gt;The following three &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/storage-mounts?pivots=aca-cli&quot;&gt;storage types&lt;/a&gt; are currently supported by Container Apps: container file system, temporary storage, and Azure Files. If you need to persist data from your application, Azure Files is the only choice. At the moment of writing, the only way (at this moment) to mount storage into the running containers is by providing a YAML file with Azure CLI. This approach is not recommended for production because of the inability to provide a declarative specification for the deployment using tools like Bicep, ARM templates, or Terraform. Using a private endpoint (Private Link) to access the storage account (Azure Files) is strongly recommended.&lt;/p&gt;

&lt;h2 id=&quot;observability&quot;&gt;Observability&lt;/h2&gt;
&lt;p&gt;Container Apps integrates out-of-the-box with the platform’s native monitoring service - Azure Monitor. Everything you write to stdout and stderr from the running containers is collected and stored in the table ContainerAppConsoleLogs_CL in Log Analytics Workspace. You can use Kusto to query the records and create an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-log&quot;&gt;Azure Monitor log alert rules&lt;/a&gt; to receive alerts when desired. For debugging purposes, you can utilize the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/observability?tabs=bash#log-streaming&quot;&gt;Log Streaming&lt;/a&gt; functionality to view the stdout and stderr messages in real time and use the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/observability?tabs=bash#container-console&quot;&gt;Container Console&lt;/a&gt; to connect to a container and perform a debug from inside. The retention period depends on the configured retention period of the Log Analytics Workspace - from 30 up to 730 days:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/log_retention.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Metrics are also &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/observability?tabs=bash#azure-monitor-metrics&quot;&gt;stored in Azure Monitor&lt;/a&gt;. In the Metrics Explorer, you can view the metrics, apply filters and splitting and create multiple charts in a single view to correlate the metrics between different Container Apps and/or services (for example, Container App + Database).&lt;/p&gt;

&lt;p&gt;Based on the metric value, you can also &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/observability?tabs=bash#azure-monitor-alerts&quot;&gt;create alerts&lt;/a&gt;. The default retention period is 93 days, and it is not extendable at this moment (not until the Diagnostic Settings are available for Container Apps).&lt;/p&gt;

&lt;h1 id=&quot;security&quot;&gt;Security&lt;/h1&gt;
&lt;h2 id=&quot;managed-identities&quot;&gt;Managed Identities&lt;/h2&gt;
&lt;p&gt;Both types of managed identities are supported: System and User assigned. Because the identity can be used to get the image from the container registry, you must always use a user-assigned identity. Ensure that the user-assigned identity is pre-provisioned and has the AcrPull permission over the Azure Container Registry before deploying the Container Apps. From within your application (container), you can call the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=portal%2Cdotnet#connect-to-azure-services-in-app-code&quot;&gt;internal REST endpoint&lt;/a&gt; to get the access token that you can use to access other OAuth2 protected services like Key Vault, Storage Account, etc. Always use the client libraries available for multiple programming languages when interacting with the REST endpoint.&lt;/p&gt;

&lt;h2 id=&quot;managing-secrets&quot;&gt;Managing Secrets&lt;/h2&gt;
&lt;p&gt;Container Apps offer &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/manage-secrets&quot;&gt;integrated secret management&lt;/a&gt; that you can use to store your secrets. You can use the secrets for storing connection strings for KEDA-based scale triggers or pass them as environment variables to the containers you deploy. Never store the secret value in the repository. Store the secrets as GitHub secrets, HashiCorp Vault, or Azure Key Vault. During the deployment phase,  read the value from the vault and pass it as a secure parameter value.&lt;/p&gt;

&lt;p&gt;Because Container Apps does not support &lt;a href=&quot;https://docs.dapr.io/developing-applications/building-blocks/secrets/secrets-overview/&quot;&gt;Dapr Secrets Management API&lt;/a&gt; it is recommended to use the built-in secret management. Otherwise, you will need to add service-specific code to handle the secrets, which will add complexity without bringing any value.&lt;/p&gt;

&lt;h2 id=&quot;built-in-auth-mechanism&quot;&gt;Built-in auth mechanism&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Auth stands for authentication + authorization&lt;/p&gt;

&lt;p&gt;Similar to Web Apps and Function Apps, Container Apps offer a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-apps/authentication&quot;&gt;built-in mechanism for authentication and authorization&lt;/a&gt; for published applications. Rather than building your auth stack in the application, it is recommended to use the built-in one.&lt;/p&gt;

&lt;p&gt;When configuring an Azure Active Directory (Microsoft) as an identity provider, the Azure AD application registration can be created automatically, or you can provide an existing (pre-created) application registration. A recommendation is to pre-created application registration using the &lt;a href=&quot;https://raw.githubusercontent.com/tosokr/container-apps-templates/main/container-apps/configure-aad-auth.sh&quot;&gt;following script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last command in the script will enable the built-in auth mechanism for the Container Apps. If you prefer to do this via the portal, all the information you need is available via variables:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/auth_portal_variables.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After the successful addition of an identity provider, the client’s (application) secret is stored in Container Apps’ secret under the name microsoft-provider-authentication-secret. If you rotate the secret, make sure you update the value before you create a new revision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Although the client secret is an optional parameter when using an existing application, always configure it because otherwise, you will end up using the implicit grant flow which these days is considered unsecured.&lt;/p&gt;

&lt;p&gt;When enabled, the built-in auth deploys a sidecar container to each replica in the revision. The sidecar container handles all the aspects of authentication and authorization, meaning there is no need to change your code to support auth. If from your code you need to know who is accessing the application, you can access that information from the following headers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;X-MS-CLIENT-PRINCIPAL-NAME&lt;/li&gt;
  &lt;li&gt;X-MS-CLIENT-PRINCIPAL-ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, any principal (user or service) can request tokens from Azure AD and access the application by presenting that token. If the behavior is not desired, you can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-restrict-your-app-to-a-set-of-users&quot;&gt;restrict the application to a set of users&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps&quot;&gt;configure application roles&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-implement-rbac-for-apps&quot;&gt;implement role-based access control in your application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;reliability&quot;&gt;Reliability&lt;/h1&gt;
&lt;h2 id=&quot;high-availability&quot;&gt;High-availability&lt;/h2&gt;
&lt;p&gt;If you deploy in a custom VNET, you have an option to enable  Availability Zones support to get a data center redundancy. In the backend, the Container Apps service creates VMSS in each Availability Zones for scheduling the applications. The Availability Zone distribution of the containers is hidden from the users. Although you have an option to disable it, as a general recommendation, always use Availability Zone deployments because they improve your overall application availability.&lt;/p&gt;

&lt;p&gt;From an application perspective, make sure you use at least two application replicas to provide high availability. Also, the event-based scaling for your application can improve availability. Why? Because if some of the containers are becoming unresponsive due to some reason, the auto-scaler will kick in and spin up new replicas to handle the load.&lt;/p&gt;

&lt;h2 id=&quot;geo-redundancy&quot;&gt;Geo-redundancy&lt;/h2&gt;
&lt;p&gt;An Azure region is a set of datacenters deployed within a latency-defined perimeter and connected through a dedicated regional low-latency network. Azure consists of 60+ regions around the world. Some regions are further divided into availability zones and unique physical locations, enabling redundancy at a regional level.&lt;/p&gt;

&lt;p&gt;Under rare circumstances, it is possible that facilities in an entire region can become inaccessible, for example, due to network failures or natural disasters. To protect against such a scenario, you can apply one of the following strategies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Redeploy on disaster:&lt;/strong&gt; In this approach, the infrastructure and application are redeployed from scratch at the time of disaster. Copy of the data is usually available in the disaster region in backups or asynchronous replications. This strategy is appropriate for non-critical applications that don’t require a guaranteed recovery time. To be successful with this strategy, your infrastructure as code scripts need to be region independent. You need to test your deployments in the disaster recovery region, for example, deploying your test environment once every month to verify that all the resource SKUs you use are also available in the DR region.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Warm Spare (Active/Passive):&lt;/strong&gt; A secondary hosted service is created in an alternate region, and roles are deployed to guarantee minimal capacity; however, the roles don’t receive production traffic. This approach is useful for applications not designed to distribute traffic across regions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hot Spare (Active/Active):&lt;/strong&gt; The application is designed to receive a production load in multiple regions. The cloud services in each region might be configured for higher capacity than required for disaster recovery purposes. Alternatively, the cloud services might scale-out as necessary during a disaster and failover. This approach requires substantial investment in application design, but it has significant benefits. These include low and guaranteed recovery time, continuous testing of all recovery locations, and efficient capacity usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Warm and Hot spare strategies for external environments, you can deploy Front Door or Traffic Manager to act as a global load balancer for your Container Apps applications:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/container-apps/geo_redundancy.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For internal environments, it is possible, although not recommended due to the complexity, to achieve active/passive geo-redundancy by using private DNS zones.&lt;/p&gt;

&lt;h2 id=&quot;service-level-agreement-sla&quot;&gt;Service Level Agreement (SLA)&lt;/h2&gt;
&lt;p&gt;Container Apps has a 99.95% SLA which translates to almost 22 minutes of acceptable downtime per month. The offered SLA is in line with the uptime SLA for AKS, which is also 99.95%. What is strange with the Container App’s SLA is that it is independent of the usage of Availability Zones. So, with or without Availability Zones support, you will get the same SLA.&lt;/p&gt;

&lt;h1 id=&quot;pricing&quot;&gt;Pricing&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/pricing/details/container-apps/&quot;&gt;Container Apps costs&lt;/a&gt; are calculated based on three meters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;vCPU seconds - number of seconds you allocate a vCPU&lt;/li&gt;
  &lt;li&gt;Gibibyte seconds - number of seconds you allocate a memory&lt;/li&gt;
  &lt;li&gt;Requests - number of processed requests (with enabled ingress only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing a proper scaling policy will reduce costs in all environments, especially on dev and test. If the Container Apps scale to zero, you don’t pay anything because they are not allocating resources or serving requests. For Container Apps replicas in idle state (resource usage below active billing threshold), you will pay ~90% less for the vCPU allocation. You may have Container Apps (replicas) in an idle state to avoid cold starts for your application.&lt;/p&gt;

&lt;p&gt;Billing at seconds granularity enables cheaper instant spikes handling. Also, there is a monthly free grant (on a subscription level) which is automatically applied.&lt;/p&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Container Apps" /><category term="Kubernetes" /><category term="ContainenerApps" /><summary type="html">Azure Container Apps is a serverless offering you can use to host your containers. It is a good fit for containerized apps and hosting microservices. Integrated services like KEDA, Envoy proxy, and Dapr provide you with out-of-the-box auto-scaling, ingress, traffic splitting, and simplified microservice connectivity. Container Apps service is built on top of Kubernetes.</summary></entry><entry><title type="html">Deep dive into Defender for Containers</title><link href="https://aztoso.com/security/defender-for-containers/" rel="alternate" type="text/html" title="Deep dive into Defender for Containers" /><published>2022-04-05T02:00:00+02:00</published><updated>2022-04-05T02:00:00+02:00</updated><id>https://aztoso.com/security/deep-dive-defender-for-containers</id><content type="html" xml:base="https://aztoso.com/security/defender-for-containers/">&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;Microsoft Defender for Containers is the new plan that merges the capabilities of the two existing Microsoft Defender for Cloud plans, Microsoft Defender for Kubernetes and Microsoft Defender for container registries, and adds a new set of features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multi-cloud support: AKS  and any Cloud Native Computing Foundation (CNCF) certified Kubernetes clusters (through Azure Arc)&lt;/li&gt;
  &lt;li&gt;Kubernetes-native deployment: automatic deployment using DaemonSet&lt;/li&gt;
  &lt;li&gt;Advanced Threat Detection: deterministic, AI, and anomaly-based detection&lt;/li&gt;
  &lt;li&gt;Vulnerability assessment: continuous scan for running images&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;provisioning&quot;&gt;Provisioning&lt;/h1&gt;

&lt;p&gt;First, the plan (the product)  needs to be provisioned on the subscription via the portal or using the REST API:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az rest &lt;span class=&quot;nt&quot;&gt;--method&lt;/span&gt; PUT &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://management.azure.com/subscriptions/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/providers/Microsoft.Security/pricings/Containers?api-version=2018-06-01&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--body&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;properties&quot;: {&quot;pricingTier&quot;: &quot;standard&quot;}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or using CLI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az security pricing create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; Containers &lt;span class=&quot;nt&quot;&gt;--tier&lt;/span&gt; Standard &lt;span class=&quot;nt&quot;&gt;--subscription&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After enabling the plan, the components can be provisioned automatically or manually on the clusters. The provisioning is related to two of the components in Defender for Containers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Defender security profile (the DaemonSet): Security profile deployed to each worker node, collects security-related data and sends it to Defender for analysis. It is required for runtime protections and security capabilities provided by Defender for Containers.&lt;/li&gt;
  &lt;li&gt;Azure policy for Kubernetes add-on: Extends Gatekeeper v3, required to apply at-scale auditing, enforcements, and safeguards on clusters in a centralized, consistent manner. The policy add-on is not an exclusive feature of Defender for Containers, but it is an integral part of the overall AKS security solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;automatic&quot;&gt;Automatic&lt;/h2&gt;

&lt;p&gt;The automatic provisioning is enabled using Azure Policies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F64def556-fbad-4622-930e-72d1d5589bf5&quot;&gt;Configure Azure Kubernetes Service clusters to enable Defender profile&lt;/a&gt; - uses a System assigned managed identity with Contributor and Log Analytics Contributor roles to remediate the cluster.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fa8eff44f-8c92-45c3-a3fb-9880802d67a7&quot;&gt;Deploy Azure Policy Add-on to Azure Kubernetes Service clusters&lt;/a&gt; - uses a System assigned managed identity with Azure Kubernetes Service Contributor Role role to remediate the cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it is based on Azure Policies, the automatic provisioning will work only for clusters that have enabled Azure RBAC (something not mentioned in the documentation). The two policies can be applied on the Management Group level, coupled with a custom policy that enables Azure RBAC on deployed clusters for an automated Defender for Container provisioning across the tenant.&lt;/p&gt;

&lt;p&gt;It can happen that both policies will try to update the cluster simultaneously. In that situation, one of the two components will not be deployed into the cluster with the following error displayed under &lt;em&gt;Deployments&lt;/em&gt; in the cluster’s resource group: &lt;em&gt;Operation is not allowed: Another operation (systemtemp - Updating) is in progress, please wait for it to finish before starting a new operation. See https://aka.ms/aks-pending-operation for more details&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A policy remediation task is needed to protect against end-users deleting the components deployments and fixing the above issue.&lt;/p&gt;

&lt;h2 id=&quot;manual&quot;&gt;Manual&lt;/h2&gt;

&lt;p&gt;The manual deployment can be done through the portal (by fixing a recommendation in Defender) or using the REST API:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az rest &lt;span class=&quot;nt&quot;&gt;--method&lt;/span&gt; PUT &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://management.azure.com/subscriptions/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/resourcegroups/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RESOURCE_GROUP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/providers/Microsoft.ContainerService/managedClusters/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLUSTER_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;?api-version=2022-01-01&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--body&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;location&quot;: &quot;'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LOCATION&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&quot;,&quot;properties&quot;: {&quot;securityProfile&quot;: {&quot;azureDefender&quot;: {&quot;enabled&quot;: true,&quot;logAnalyticsWorkspaceResourceId&quot;: &quot;'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$logAnalyticsWorkspaceResourceId&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&quot;}}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;features&quot;&gt;Features&lt;/h1&gt;
&lt;h2 id=&quot;scanning-images-for-vulnerability&quot;&gt;Scanning images for vulnerability&lt;/h2&gt;

&lt;p&gt;Defender for Containers scans images for vulnerabilities stored in an ACR. It pulls the image from the registry and runs it in an isolated sandbox with the Qualys scanner. Image scan is triggered for every image that is pushed, pulled, or imported into the registry. In addition, the recently (in the last 30 days) pulled images are scanned weekly. If the defender security profile (the DaemonSet) is deployed in a cluster, it will initiate a weekly scan of the running images pulled from ACR.&lt;/p&gt;

&lt;p&gt;The vulnerabilities findings are &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-container-registries-usage#view-and-remediate-findings&quot;&gt;listed under Recommendations&lt;/a&gt; and are visible a few seconds after the scan finishes.&lt;/p&gt;

&lt;p&gt;Available in private preview, there is a policy named &lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F13cd7ae3-5bc0-4ac4-a62d-4f7c120b9759&quot;&gt;Kubernetes clusters should gate deployment of vulnerable images&lt;/a&gt; which can deny the deployment of images with vulnerabilities identified by the CI/CD scanning or ACR scanning. To make use of it, the Azure Policy add-on must be installed on the cluster.&lt;/p&gt;

&lt;p&gt;There is a possibility to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-container-registries-usage#disable-specific-findings&quot;&gt;disable specific findings&lt;/a&gt; based on multiple parameters, including severity and CVSS (Common Vulnerability Scoring System) score. That will prevent unnecessary noise and enable us to focus only on the findings that need action.&lt;/p&gt;

&lt;h2 id=&quot;cicd-integration&quot;&gt;CI/CD integration&lt;/h2&gt;

&lt;p&gt;For Github workflows, there is a Azure/container-scan action that scans the images for vulnerabilities using Trivy and performs static code analysis using Dockle. The results from the scans can be pushed to the Defender for Containers using an Application Insight. The complete guideline is available &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-container-registries-cicd&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As it is documented, the integration can improve visibility. It only makes sense if it is combined with the policy &lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F13cd7ae3-5bc0-4ac4-a62d-4f7c120b9759&quot;&gt;Kubernetes clusters should gate deployment of vulnerable images&lt;/a&gt;. Another option is to parse the output and not push to the ACR images with specific vulnerabilities or best practices violations.&lt;/p&gt;

&lt;h2 id=&quot;runtime-security&quot;&gt;Runtime security&lt;/h2&gt;

&lt;p&gt;Defender for Containers provides real-time threat protection and generates alerts for suspicious activities. Threat protection at the cluster level is provided by the Defender profile (the DaemonSet) and analysis of the Kubernetes audit logs.&lt;/p&gt;

&lt;p&gt;The node-level threat detection, DaemonSet that runs on all of the nodes in the cluster, inspects activity on the Kubernetes worker-node to detect suspicious activity that runs by the containers on the nodes. The solution is developed for Kubernetes and provides container-specific alerts and vulnerabilities detections as they are discovered. It also tracks the &lt;a href=&quot;https://attack.mitre.org/matrices/enterprise/containers/&quot;&gt;MITRE ATT&amp;amp;CK®&lt;/a&gt; matrix for Containers, which is a collection of tactics and techniques of attacks related to container environments.&lt;/p&gt;

&lt;p&gt;The Defender profile deploys the following components into the cluster:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/defender/containers_table.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although initial resource requests are low, proper testing is needed because the total max limits for the DaemonSets per node are: 270m CPU and 328Mi memory.&lt;/p&gt;

&lt;p&gt;When Defender for Container is enabled on a cluster, it will monitor the Kubernetes API operations to find suspicious and malicious activities in the Kubernetes control plane. Under the hood, Defender collects the API logs over the Azure backbone, analyzes those, and presents the findings. The process of collecting and analyzing is transparent and there is no cost associated with storing the API logs.&lt;/p&gt;

&lt;h2 id=&quot;security-alerts&quot;&gt;Security Alerts&lt;/h2&gt;

&lt;p&gt;Microsoft Defender for Containers provides &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/defender-for-cloud/alerts-reference#alerts-k8scluster&quot;&gt;security alerts&lt;/a&gt; categorized in three severity levels: High, Medium, and Low. Fired alerts are visible in the Security alerts sections in Defender for Cloud.  &lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/EnvironmentSettings&quot;&gt;Email notifications&lt;/a&gt; are also configurable, based on the severity level, to the chosen email recipients (by default to the owners of the subscription). Microsoft has an &lt;a href=&quot;https://splunkbase.splunk.com/app/4564/&quot;&gt;add-on&lt;/a&gt; that uses &lt;a href=&quot;https://docs.microsoft.com/en-us/graph/api/resources/security-api-overview?view=graph-rest-1.0&quot;&gt;Microsoft Graph Security API&lt;/a&gt; to ingest the security alerts into Splunk. The security alerts and recommendations are also exportable to an Event Hub from where they can be consumed by Splunk using the  &lt;a href=&quot;https://splunkbase.splunk.com/app/3110/#/overview&quot;&gt;Splunk add-on for Microsoft Cloud Services&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;pricing&quot;&gt;Pricing&lt;/h1&gt;

&lt;p&gt;Microsoft Defender for Container is list priced $7 per vCore associated with a Kubernetes cluster. Please note that vCore differs from vCPU for the SKUs that support hyper-threading (D series, for example), and the ratio is 2:1 (vCPU:vCore). The average number of vCores in the last 30 days is taken into account for the monthly calculations. The price also includes 20 free ACR vulnerabilities scans per vCore. Any additional scan is list priced at $0.29 per image digest.&lt;/p&gt;

&lt;p&gt;A workbook for cost estimation across multiple subscriptions is available on &lt;a href=&quot;https://github.com/Azure/Microsoft-Defender-for-Cloud/tree/main/Workbooks/Defender%20for%20Containers%20Cost%20Estimation&quot;&gt;GitHub&lt;/a&gt;. The workbook supports a maximum of 200 subscriptions. To generate the cost estimate for bigger tenants, use the following Resource Graph Query:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;resources
| where type =~ &quot;microsoft.containerservice/managedclusters&quot;
| extend machineType = tostring(properties.agentPoolProfiles[0].vmSize), nodesCount = toint(properties.agentPoolProfiles[0]['count']), powerState = tostring(properties.agentPoolProfiles[0].powerState.code), minMachineCount = toint(properties.agentPoolProfiles[0].minCount), maxMachineCount = toint(properties.agentPoolProfiles[0].maxCount)
| extend vCoresPerMachine = toint(extract_all(@&quot;(\d+)&quot;, machineType)[0])
| extend coresTotal = vCoresPerMachine * nodesCount
| extend minMonthlyCost = vCoresPerMachine * minMachineCount * 7, maxMonthlyCost = vCoresPerMachine * maxMachineCount * 7, estimatedMonthlyCost = coresTotal * 7
| summarize sum(estimatedMonthlyCost)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Defender for Containers can significantly improve the security of the container environments. The solution can be automatically provisioned to the newly created and already existing clusters through the (Azure) platform. People with Security admin and Security reader roles on the subscriptions can view their security recommendations and alerts and have an active role in the security observation of the deployed resources. A single support contact point covers both the product and the underlying infrastructure in case of an issue.&lt;/p&gt;

&lt;p&gt;Pushed, pulled, and imported images to ACR will be scanned for vulnerabilities with Qualys. On top of it, if used, the CI/CD integration will add scan results based on Trivy and security best practices.  With the runtime integration, the images running in Kubernetes will be scanned every week for any newly discovered vulnerabilities. At this moment, there is no support for scanning running images pulled from public repositories. Microsoft has that feature in the backlog.&lt;/p&gt;

&lt;p&gt;The runtime security doesn’t provide an inventory list of pods running in the cluster. For environments with many clusters, having that central list of running images accross the tenant is very useful. Today, to get that inventory list yet another agent need to be deployed into the cluster - the Log Analytics agent.&lt;/p&gt;

&lt;p&gt;The Azure Policy-addon for Kubernetes is required in order to get the maximum out of Defender for Containers, like denying deployment of vurnelable images. The policy add-on will apply all of the policies applicable to the resource, coming from the management group, subscription and resource group. This can lead to an unexpected behaivour, because Kubernetes related policies are part of different policy initiatives that maybe are already applied at the subscription level.&lt;/p&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Azure Kubernetes Service" /><category term="Security" /><category term="Kubernetes" /><category term="Defender" /><summary type="html">Microsoft Defender for Containers is the new plan that merges the capabilities of the two existing Microsoft Defender for Cloud plans, Microsoft Defender for Kubernetes and Microsoft Defender for container registries, and adds a new set of features like multi-cloud support, Kubernetes-native deployment, Advanced Threat Detection and vulnerability assessment</summary></entry><entry><title type="html">OAuth 2.0 with Managed Identities</title><link href="https://aztoso.com/security/oauth2-managed-identities/" rel="alternate" type="text/html" title="OAuth 2.0 with Managed Identities" /><published>2022-03-23T01:00:00+01:00</published><updated>2022-03-23T01:00:00+01:00</updated><id>https://aztoso.com/security/oauth2-managed-identities</id><content type="html" xml:base="https://aztoso.com/security/oauth2-managed-identities/">&lt;p&gt;Using Managed Identities to access an OAuth 2.0 protected application is a best practice for an application to application communication or, as referred to in the OAuth 2.0 terminology - Client Credentials Grant Flow. This article will show you how to configure your application in Azure AD and use Managed Identity to access the application.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols&quot;&gt;Microsoft identity platform (Azure Active Directory)&lt;/a&gt; offers authentication and authorization services using standards-compliant implementations of OAuth 2.0 and OpenID Connect (OIDC) 1.0. You can use Azure Active Directory to build an authentication and authorization layer for your application. There are multiple OAuth 2.0 flows implementation, mainly depending on the type of the client application. The Client Credentials Flow is the best fit when it comes to service-to-service communication.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow&quot;&gt;OAuth 2.0 Client Credentials Grant Flow&lt;/a&gt; permits a web service (serving the role of a confidential client) to use its credentials to authenticate when calling another web service instead of impersonating a user. In this scenario, the client is typically a middle-tier web service, a daemon service, or a website:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/aad/clientCredentialsFlow.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The client application authenticates to the Azure AD token issuance endpoint and requests an access token.&lt;/li&gt;
  &lt;li&gt;The Azure AD token issuance endpoint issues the access token.&lt;/li&gt;
  &lt;li&gt;The access token is used to authenticate to the secured resource.&lt;/li&gt;
  &lt;li&gt;Data from the secured resource is returned to the client application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Authentication is performed using the OpenId Connect protocol on top of OAuth2. The client uses a service principal with a secret or certificate to authenticate to Azure AD. &lt;strong&gt;A recommended approach for the clients is to use Managed Identities&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Eliminate the need for users having to manage credentials&lt;/li&gt;
  &lt;li&gt;Non exportable certificate-based credentials, valid for 90 days, rolled after 45 days&lt;/li&gt;
  &lt;li&gt;Uses long-lived tokens (24 hours) with a proactive token refresh every 12 hours (it can handle a maximum Azure AD downtime between 12-24 hours)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To register your application in Azure AD, you need to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a manifest.json file that describes the roles your service support. Based on the provided role in the token, you can perform Role Base Access Control in your service:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; manifest.json &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
 [{
     &quot;allowedMemberTypes&quot;: [
     &quot;Application&quot;
     ],
     &quot;description&quot;: &quot;Consumers can use the service&quot;,
     &quot;displayName&quot;: &quot;Consumer&quot;,
     &quot;isEnabled&quot;: &quot;true&quot;,
     &quot;value&quot;: &quot;consumer&quot;
 }]
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt; ENDOFFILE
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Register an application in Azure AD that represents your service:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# Register an application in Azure AD&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;appId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az ad app create &lt;span class=&quot;nt&quot;&gt;--display-name&lt;/span&gt; aztoso-com-app &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--identifier-uris&lt;/span&gt; https://myapp.aztoso.com &lt;span class=&quot;nt&quot;&gt;--app-roles&lt;/span&gt; @manifest.json &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; appId&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# Install the application in the local Azure AD - by creating a service principal&lt;/span&gt;
 az ad sp create &lt;span class=&quot;nt&quot;&gt;--id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$appId&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Assign a role to the User Managed Identity client will use to connect to your service:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;#Object ID of your application registered in Azure AD&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;appId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;YOUR_APPPLICATION_ID_HERE&amp;gt;&quot;&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# ObjectId of the Managed Identity used by the client&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;managedIdentityObjectId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;OBJECT_ID_OF_MANAGED_IDENTITY&amp;gt;&quot;&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# Role you want to assign&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;consumer&quot;&lt;/span&gt;
    
 &lt;span class=&quot;c&quot;&gt;# Get the application role id&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;appRoleId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az ad app show &lt;span class=&quot;nt&quot;&gt;--id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$appId&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; appRoles[?appRoles.value&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;.id &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# Application's service principal id&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;spObjectId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az ad sp show &lt;span class=&quot;nt&quot;&gt;--id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$appId&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; objectId &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# Create the body for the Graph API call&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; body.json &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
 {
 &quot;principalId&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;managedIdentityObjectId&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;,
 &quot;resourceId&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;spObjectId&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;,
 &quot;appRoleId&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;appRoleId&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
 }
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt; ENDOFFILE
&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Assign the role to the managed identity&lt;/span&gt;
 az rest &lt;span class=&quot;nt&quot;&gt;--method&lt;/span&gt; POST &lt;span class=&quot;nt&quot;&gt;--uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://graph.microsoft.com/v1.0/servicePrincipals/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$managedIdentityObjectId&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/appRoleAssignedTo&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--headers&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Content-Type=application/json'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--body&lt;/span&gt; @body.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After assigning the necessary role to the Managed Identity, the client can get the token from the Azure Instance Metadata Service and use the token to access your service:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s1&quot;&gt;'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&amp;amp;resource=https%3A%2F%2Fmyapp.aztoso.com'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; Metadata:true &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; .access_token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The issued token contains claims that you can use to authorize clients to your service:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;aud - Identifies the intended recipient of the token. Your service should validate this value, and reject the token if the value does not match.&lt;/li&gt;
  &lt;li&gt;iss - Identifies the security token service (STS) that constructs and returns the token, and the Azure AD tenant in which the user was authenticated. Your service should use the GUID portion of the claim to restrict the set of tenants that can sign in to the app.&lt;/li&gt;
  &lt;li&gt;appid -  The application ID of the client using the token. Your service should use appid or oid to identify the client that connects to your service.&lt;/li&gt;
  &lt;li&gt;oid - The immutable identifier for an object in the Microsoft identity system - the Object (principal) ID. Your service should use appid or oid to identify the client that connects to your service.&lt;/li&gt;
  &lt;li&gt;roles - The set of permissions exposed by your application that the requesting application has been given permission to call. If implementing RBAC, your service should validate the roles of the calling client.&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Security" /><category term="OAuth2" /><summary type="html">Using Managed Identities to access an OAuth 2.0 protected application is a best practice for an application to application communication or, as referred to in the OAuth 2.0 terminology - Client Credentials Grant Flow. This article will show you how to configure your application in Azure AD and use Managed Identity to access the application.</summary></entry><entry><title type="html">Private Link and Flexible Server DNS resolution using custom VNET resolvers</title><link href="https://aztoso.com/networking/dns-private-link-flexible-server/" rel="alternate" type="text/html" title="Private Link and Flexible Server DNS resolution using custom VNET resolvers" /><published>2022-02-10T01:00:00+01:00</published><updated>2022-02-10T01:00:00+01:00</updated><id>https://aztoso.com/networking/aks-connect-to-flexible-server</id><content type="html" xml:base="https://aztoso.com/networking/dns-private-link-flexible-server/">&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;In enterprise scenarios, when you need to resolve on-premise DNS records or have cross-subscription DNS resolution of the private DNS zones, you need to have some kind of hybrid DNS configuration. One such setup is having the DNS resolvers in a central subscription with all Private DNS zones linked to the VNET those resolvers reside in.&lt;/p&gt;

&lt;h1 id=&quot;private-link-endpoints&quot;&gt;Private (Link) Endpoints&lt;/h1&gt;

&lt;p&gt;If you plan to create many private endpoints across your subscriptions, the above approach will not scale. A better solution is to have a single private DNS zone per service linked to the resolvers VNET and add the records there during the endpoint creation. To make it work, after you create the private endpoint for the service in your VNET you need to create a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-dns#private-dns-zone-group&quot;&gt;DNS Zone Group&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az network private-endpoint dns-zone-group create 
    &lt;span class=&quot;nt&quot;&gt;--endpoint-name&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# the name of the enpoint you created&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# name of the dns-zone-group (free to use whatever you like)&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--private-dns-zone&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# resource id of the private DNS zone&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# resource group where you created the private endpoint&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--zone-name&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# name of the private DNS zone&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--subscription&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# if endpoint is created in another subscription&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# example&lt;/span&gt;
az network private-endpoint dns-zone-group create 
    &lt;span class=&quot;nt&quot;&gt;--endpoint-name&lt;/span&gt; privatelink-storage &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; rg-privatelink-test &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; test-dns-zone-group &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--private-dns-zone&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/subscriptions/&amp;lt;SUBSCRIPTION_ID&amp;gt;/resourceGroups/rg-test-privatelink/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--zone-name&lt;/span&gt; privatelink.blob.core.windows.net
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To be able to create that DNS Zone Group, you need the following permissions on the private DNS zone:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Network/privateDnsZones/A/read&quot;&lt;/span&gt;,
&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Network/privateDnsZones/A/write&quot;&lt;/span&gt;,
&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Network/privateDnsZones/read&quot;&lt;/span&gt;,
&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Network/privateDnsZones/join/action&quot;&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To create the Private DNS Zone Groups through the portal, you also need the &lt;em&gt;Microsoft.Resources/subscriptions/resourceGroups/read&lt;/em&gt; permission on the resource group hosting the Private DNS Zone.&lt;/p&gt;

&lt;h1 id=&quot;flexible-server&quot;&gt;Flexible Server&lt;/h1&gt;

&lt;p&gt;Flexible Server uses VNET integration to provide private access to the databases. Under the hood, when you create the service, you delegate a dedicated subnet to Microsoft to deploy and operate the VMs used for hosting the database server for you. As part of the VNET integration, the deployment creates a Private DNS zone with the name equal to the server’s name, making it impossible to use the same solution as with the private endpoints. The only possibility to overcome this challenge is to host custom DNS resolvers in the (spoke) subscriptions:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/networking/vnet-custom-resolvers.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;dns-resolution-from-aks&quot;&gt;DNS resolution from AKS&lt;/h2&gt;

&lt;p&gt;Suppose you only need to connect to the Flexible Server from AKS. In that case, instead of deploying custom DNS resolvers in the VNET, you can &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/coredns-custom&quot;&gt;customize CoreDNS&lt;/a&gt; to forward all the queries for Flexible Server to the Azure DNS Server. For PostgreSQL, the custom configuration will look like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-custom
  namespace: kube-system
data:
  flexible.server: |
   postgres.database.azure.com:53 {
      errors
      cache 30
      forward . 168.63.129.16
      reload
    }
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After you apply the coredns-custom configmap, force CoreDNS to reload the ConfigMap (as per the documentation, the command bellow will not cause any downtime):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl delete pod &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; kube-system &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; k8s-app&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;kube-dns
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Azure Kubernetes Service" /><category term="Networking" /><category term="AKS" /><category term="FlexibleServer" /><summary type="html">In enterprise scenarios, when you need to resolve on-premise DNS records or have cross-subscription DNS resolution of the private DNS zones, configuring proper DNS resolution for Private (Link) Endpoints and Flexible Server resources can be challenging. In a nutshell, for Private Link resources, use single DNS zones hosted in a central subscription. In contrast, use custom DNS resolvers for Flexible Servers in each subscription or apply custom CoreDNS configuration for resolution from AKS clusters.</summary></entry><entry><title type="html">Building an AKS baseline architecture - Part 4 - AAD Pod Identity</title><link href="https://aztoso.com/aks/baseline-part-4/" rel="alternate" type="text/html" title="Building an AKS baseline architecture - Part 4 - AAD Pod Identity" /><published>2022-01-04T01:00:00+01:00</published><updated>2022-01-04T01:00:00+01:00</updated><id>https://aztoso.com/aks/building-aks-baseline-part-4</id><content type="html" xml:base="https://aztoso.com/aks/baseline-part-4/">&lt;p&gt;Posts from this series:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-1/&quot;&gt;Building an AKS baseline architecture - Part 1 - Cluster creation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-2/&quot;&gt;Building an AKS baseline architecture - Part 2 - Governance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-3/&quot;&gt;Building an AKS baseline architecture - Part 3 - GitOps with Flux2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-4/&quot;&gt;Building an AKS baseline architecture - Part 4 - AAD Pod Identity&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;When we develop solutions, they usually include multiple Azure services that need to communicate between them. Moving the compute part of the solution to AKS leaves the same problem - who are we going to authenticate the communication between those Azure services? With the traditional approach, using service principals or connection keys, we have the problem of safely storing and rotating the credentials. Microsoft solved this problem by introducing Managed Identities. We can use Managed Identities for the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/use-managed-identity&quot;&gt;AKS Control plane and Kubelet&lt;/a&gt;, but can we use them for pods inside AKS? The answer is YES - with the open-source component named &lt;a href=&quot;https://github.com/Azure/aad-pod-identity&quot;&gt;AAD Pod Identity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AAD Pod Identity enables Kubernetes applications to access cloud resources securely with Azure Active Directory. In addition to the security benefit, Managed Identities uses long-lived tokens and can handle Azure AD hiccups with a maximum duration between 12 and 24 hours.&lt;/p&gt;

&lt;p&gt;Note:  In preview, a managed add-on is available that you can enable for your AKS cluster, which will provide you with the same functionality. However, this add-on will never transition to General Availability because Microsoft is working on a V2 version.&lt;/p&gt;

&lt;h1 id=&quot;how-it-works&quot;&gt;How it works?&lt;/h1&gt;

&lt;p&gt;The solution consists of two core components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Managed Identity Controller (MIC) - manage the AzureAssignedIdentity. Assigns the specified Users Managed Identity to the nodepool (VMSS) on a pod scheduling event.&lt;/li&gt;
  &lt;li&gt;Node Managed Identity (NMI) - intercept the requests to the Azure Instance Metadata Service and get the correct token based on the AzureIdentityBindings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and four Custom Resource Definitions (CRDs):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AzureIdentity - describes the Azure Identity resource. It supports: User Assigned Identity and Service Principal (with password or certificate)&lt;/li&gt;
  &lt;li&gt;AzureIdentityBindings - enables binding of the AzureIdentity to a pod using the specified selector&lt;/li&gt;
  &lt;li&gt;AzureAssignedIdentity -  managed by the Managed Identity Controller, it describes the state of the identity binding&lt;/li&gt;
  &lt;li&gt;AzurePodIdentityException - enables pods to access the Azure Instance Metadata Service directly, without the request being intercepted by NMI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following diagram describe the complete workflow of how AAD Pod Identity works:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/aks/aad-pod-identity.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;role-based-access-control-assignments&quot;&gt;Role Based Access Control Assignments&lt;/h1&gt;

&lt;p&gt;The AKS cluster needs to have the necessary permissions to assign and unassign the user-managed identities on the underlying Virtual Machine Scale Set. You need to have the role of Owner or User Access Administrator to perform RBAC assignments in Azure.&lt;/p&gt;

&lt;p&gt;To create the assignments using Azure CLI, use the instructions below. Consider automating the process and do the assignments using a pipeline:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;RESOURCE_GROUP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# resource group where the AKS cluster is deployed&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CLUSTER_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# the name of the AKS cluster&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#  the id of the subscription&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IDENTITIES_RESOURCE_GROUP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# resource group name where the identities you plan to assign to pods are stored. If you plan to bind their lifetime to the cluster, you can keep them in the NODE_RESOURCE_GROUP. Otherwise, it is a best practice to use a dedicated resource group&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Obtain the ID of the managed identity assigned on the node pools&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CLUSTER_IDENTITY_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az aks show &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$RESOURCE_GROUP&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLUSTER_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; identityProfile.kubeletidentity.clientId &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Get the node resource group&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;NODE_RESOURCE_GROUP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az aks show &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$RESOURCE_GROUP&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLUSTER_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; nodeResourceGroup &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv &lt;span class=&quot;nt&quot;&gt;--only-show-errors&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Assign the necessary role to the AKS cluster identity, to be able to modify the VMSS settings&lt;/span&gt;
az role assignment create &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Virtual Machine Contributor&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLUSTER_IDENTITY_ID&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; /subscriptions/&lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;/resourcegroups/&lt;span class=&quot;nv&quot;&gt;$NODE_RESOURCE_GROUP&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Option 1 - Assign the necessary role to the AKS cluster identity, to be able to attach the managed identities in the IDENTITIES_RESOURCE_GROUP to the VMSS.&lt;/span&gt;
az role assignment create &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Managed Identity Operator&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLUSTER_IDENTITY_ID&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; /subscriptions/&lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;/resourcegroups/&lt;span class=&quot;nv&quot;&gt;$IDENTITIES_RESOURCE_GROUP&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Option 2 - Assing the necessary role to the AKS cluster identity only to particular user managed identities:&lt;/span&gt;
az role assignment create &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Managed Identity Operator&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLUSTER_IDENTITY_ID&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; /subscriptions/&lt;span class=&quot;nv&quot;&gt;$SUBSCRIPTION_ID&lt;/span&gt;/resourcegroups/&lt;span class=&quot;nv&quot;&gt;$IDENTITIES_RESOURCE_GROUP&lt;/span&gt;/providers/Microsoft.ManagedIdentity/userAssignedIdentities/&lt;span class=&quot;nv&quot;&gt;$IDENTITY_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;security-considerations-with-kubenet&quot;&gt;Security considerations with Kubenet&lt;/h1&gt;

&lt;p&gt;Running aad-pod-identity in a cluster with Kubenet network plugin is not a recommended configuration because of the security implication. Kubenet is susceptible to ARP spoofing, making it possible for other pods to impersonate a pod with access to identity. Using CAP_NET_RAW capability, an attacker pod could impersonate a pod with valid access and then request a token.&lt;/p&gt;

&lt;p&gt;To mitigate the vulnerability, you need to add a securityContext in your pod defintion that drops the NET_RAW capability:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NET_RAW&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To enforce this mitigation at the cluster level, use &lt;a href=&quot;https://aztoso.com/aks/baseline-part-2/#azure-policies&quot;&gt;Azure Policy add-on for AKS&lt;/a&gt; and enroll the &lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fc26596ff-4d70-4e6a-9a30-c2506bd2f80c&quot;&gt;&lt;em&gt;Kubernetes cluster containers should only use allowed capabilities&lt;/em&gt;&lt;/a&gt; policy. Add NET_RAW into &lt;em&gt;Required drop capabilities&lt;/em&gt; in the policy parameter values.&lt;/p&gt;

&lt;h1 id=&quot;deploy-with-flux2&quot;&gt;Deploy with Flux2&lt;/h1&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PULL THE HELM CHART LOCALY&lt;/span&gt;
helm repo add aad-pod-identity https://raw.githubusercontent.com/Azure/aad-pod-identity/master/charts
helm pull aad-pod-identity/aad-pod-identity

&lt;span class=&quot;c&quot;&gt;# PUSH THE HELM CHART TO ACR&lt;/span&gt;
az acr helm push &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; aad-pod-identity-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.tgz

&lt;span class=&quot;c&quot;&gt;# PUSH THE IMAGE TO ACR&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;AADPODIDENTITY_CHART_VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;helm show chart aad-pod-identity/aad-pod-identity | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;version |  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; mcr.microsoft.com/oss/azure/aad-pod-identity/nmi:v&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;helm show chart aad-pod-identity/aad-pod-identity | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;appVersion |  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; mcr.microsoft.com/oss/azure/aad-pod-identity/mic:v&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;helm show chart aad-pod-identity/aad-pod-identity | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;appVersion |  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# CREATE HELMRELEASE MANIFEST FOR AAD-POD-IDENTITY&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/aad-pod-identity
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/aad-pod-identity/release.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: aad-pod-identity
spec:
  releaseName: aad-pod-identity
  chart:
    spec:
      chart: aad-pod-identity
      sourceRef:
        kind: HelmRepository
        name: acr-private
        namespace: flux-system
      version: &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;AADPODIDENTITY_CHART_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
  interval: 1h0m0s
  install:
    remediation:
      retries: 3
  # Default values
  # https://github.com/Azure/aad-pod-identity/blob/master/charts/aad-pod-identity/values.yaml
  values:    
    image:
      repository: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ACR_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.azurecr.io/oss/azure/aad-pod-identity
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# KUSTOMIZATION MANIFEST FOR AAD-POD-IDENTITY&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/aad-pod-identity/kustomization.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: baseline
resources:  
  - release.yaml
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# COMMIT AND PUSH THE CHANGES&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;
git add &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aad-pod-identity installation&quot;&lt;/span&gt;
git push
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Azure Kubernetes Service" /><category term="AKS" /><summary type="html">In this series of posts, you will find all the steps needed to build a baseline or reference architecture for Azure Kubernetes Service (AKS) by incorporating all the best practices from the operations and governance perspective. In this post, we will explore one of the core baseline componets - AAD Pod Identity.</summary></entry><entry><title type="html">Building an AKS baseline architecture - Part 3 - GitOps with Flux2</title><link href="https://aztoso.com/aks/baseline-part-3/" rel="alternate" type="text/html" title="Building an AKS baseline architecture - Part 3 - GitOps with Flux2" /><published>2021-10-16T02:00:00+02:00</published><updated>2021-10-16T02:00:00+02:00</updated><id>https://aztoso.com/aks/building-aks-baseline-part-3</id><content type="html" xml:base="https://aztoso.com/aks/baseline-part-3/">&lt;p&gt;Posts from this series:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-1/&quot;&gt;Building an AKS baseline architecture - Part 1 - Cluster creation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-2/&quot;&gt;Building an AKS baseline architecture - Part 2 - Governance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-3/&quot;&gt;Building an AKS baseline architecture - Part 3 - GitOps with Flux2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/aks/baseline-part-4/&quot;&gt;Building an AKS baseline architecture - Part 4 - AAD Pod Identity&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;what-is-gitops&quot;&gt;What is GitOps?&lt;/h1&gt;

&lt;p&gt;“&lt;em&gt;GitOps is a way of implementing Continuous Deployment for cloud native applications. It focuses on a developer-centric experience when operating infrastructure, by using tools developers are already familiar with, including Git and Continuous Deployment tools.
The core idea of GitOps is having a Git repository that always contains declarative descriptions of the infrastructure currently desired in the production environment and an automated process to make the production environment match the described state in the repository. If you want to deploy a new application or update an existing one, you only need to update the repository - the automated process handles everything else. It’s like having cruise control for managing your applications in production.&lt;/em&gt;”- &lt;a href=&quot;https://www.gitops.tech/&quot;&gt;GitOps.tech&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a great &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/architecture/example-scenario/gitops-aks/gitops-blueprint-aks&quot;&gt;GitOps for AKS&lt;/a&gt; document available in the Azure Architecture Center that can help you learn more about the need and the benefits of using the GitOps approach for AKS. In this article, I will focus on the implementation part of GitOps - how to make it work with Flux.&lt;/p&gt;

&lt;h1 id=&quot;flux&quot;&gt;Flux&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://fluxcd.io&quot;&gt;Flux&lt;/a&gt; is a tool for keeping Kubernetes clusters in sync with sources of configuration (like Git repositories), and automating updates to configuration when there is new code to deploy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are multiple approaches for organizing the git repos when enrolling Flux. In the baseline architecture, we will implement the &lt;a href=&quot;https://fluxcd.io/docs/guides/repository-structure/#monorepo&quot;&gt;monorepo&lt;/a&gt; approach - a single repository for multiple environments. Environments will have the same base infrastructure components,  but the versions of custom applications will be different:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;├── apps
│   ├── production 
│   └── staging
├── infrastructure
│   ├── sources
│   ├── base 
└── clusters
    ├── production
    └── staging
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The design is not optimal for enterprise deployments, but it is a good starting point for learning.&lt;/p&gt;

&lt;p&gt;As a best practice, you should accommodate the trank-based development approach: create minor updates in short-living branches that you will merge to main by opening pull requests. For the simplicity of the demo, we will perform all commits directly to our main branch.&lt;/p&gt;

&lt;h2 id=&quot;install-flux&quot;&gt;Install Flux&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# PERSONAL ACCESS TOKEN FOR GITHUB. SCOPE: Full control of private repositories&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;aks-baseline-clusters &lt;span class=&quot;c&quot;&gt;# REPO FOR FLUX&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;clusters/production &lt;span class=&quot;c&quot;&gt;# PATH TO USE INSIDE THE REPO &lt;/span&gt;

curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; https://fluxcd.io/install.sh | &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;bash &lt;span class=&quot;c&quot;&gt;# INSTALL FLUX LOCALY&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# ENABLE COMPLETITIONS in ~/.bash_profile&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;flux completion bash&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# IMPORT THE FLUX IMAGES IN OUR AZURE CONTAINER REGISTRY (https://aztoso.com/aks/baseline-part-2/#azure-container-registry-acr)&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;flux &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--export&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ghcr.io/fluxcd/helm-controller | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;flux &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--export&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ghcr.io/fluxcd/kustomize-controller | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;flux &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--export&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ghcr.io/fluxcd/notification-controller | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;flux &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--export&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ghcr.io/fluxcd/source-controller | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# BOOTSTRAP FLUX&lt;/span&gt;
flux bootstrap github &lt;span class=&quot;nt&quot;&gt;--owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GITHUB_USER&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GITHUB_PATH&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--personal&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--registry&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt;.azurecr.io/fluxcd

&lt;span class=&quot;c&quot;&gt;# CLONE THE REPO LOCALY&lt;/span&gt;
git clone https://&lt;span class=&quot;nv&quot;&gt;$GITHUB_USER&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$GITHUB_TOKEN&lt;/span&gt;@github.com/&lt;span class=&quot;nv&quot;&gt;$GITHUB_USER&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;.git  ../&lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;

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

&lt;p&gt;We will use Azure Container Registry to host our Helm Charts. Because OCI is &lt;a href=&quot;https://fluxcd.io/docs/use-cases/azure/#helm-repositories-on-azure-container-registry&quot;&gt;not yet supported by Flux&lt;/a&gt;, we can’t use Managed Identities for authenticating to the ACR. The only option is to use service principal (make sure you regulary rotate the secret):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# CREATE A SERVICE PRINCIPAL WITH PERMISSION TO PULL FROM THE ACR&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;fluxHelmAcr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az ad sp create-for-rbac &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; sp-aks-baseline-&lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-acrpull&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AcrPull&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az acr show &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$RESOURCE_GROUP_ACR&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv &lt;span class=&quot;nt&quot;&gt;--only-show-errors&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;FLUX_HELM_ACR_APPID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fluxHelmAcr&lt;/span&gt; | jq &lt;span class=&quot;s1&quot;&gt;'.appId'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\n'&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;FLUX_HELM_ACR_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fluxHelmAcr&lt;/span&gt; | jq &lt;span class=&quot;s1&quot;&gt;'.password'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\n'&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# KUBERNETES SECRET THAT STORES THE SERVICE PRINCIPAL DETAILS&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: flux-helm-acr-credentials
  namespace: flux-system
type: Opaque
data:
  username: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLUX_HELM_ACR_APPID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  password: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLUX_HELM_ACR_SECRET&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# THE KUSTOMIZATION DEFINITION FOR THE CLUSTER:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/clusters/production
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/clusters/production/infrastructure.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
  name: infrastructure
  namespace: flux-system
spec:
  interval: 10m0s
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./infrastructure
  prune: true
  validation: client
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;
git add &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;infrastructure production kustomization&quot;&lt;/span&gt;
git push
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..

&lt;span class=&quot;c&quot;&gt;# CREATE HELMREPOSITORY MANIFEST FOR THE ACR&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/sources
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/sources/acr.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
  name: acr-private
  namespace: flux-system
spec:
  url: https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ACR_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.azurecr.io/helm/v1/repo
  secretRef:
    name: flux-helm-acr-credentials
  interval: 1m
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# COMMIT AND PUSH THE CHANGES&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;
git add &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;acr helm repository&quot;&lt;/span&gt;
git push
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To verify that  Flux created the Helm repository definition on the cluster, execute the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get helmrepositories.source.toolkit.fluxcd.io &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NAME          URL                                                READY   STATUS                                                       AGE
acr-private   https://xw5OUsrKQmU40Im4.azurecr.io/helm/v1/repo   True    Fetched revision: c45818b304c4b776703ab0fc56efbe492690830e   3h9m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;sample-kured-helm-deployment-using-flux&quot;&gt;Sample Kured Helm deployment using Flux&lt;/h1&gt;

&lt;p&gt;Kured (Kubernetes Reboot Daemon) is a DaemonSet that triggers a node reboot if a file exists at the predefined path. The underlying OS in AKS, Ubuntu 18.04, performs a security or kernel patch check every night and automatically installs the patches. If the patch requires a restart, AKS will create a/var/run/reboot-required file. In simple words, Kured makes sure that all critical updates are installed on the cluster.&lt;/p&gt;

&lt;p&gt;Note: Kured needs to be configured with the following tolerations to run on the dedicated system nodes:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;na&quot;&gt;tolerations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CriticalAddonsOnly&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Deploy Kured using HelmRelease and Kustomization:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PULL THE HELM CHART LOCALY&lt;/span&gt;
helm repo add kured https://weaveworks.github.io/kured
helm pull kured/kured

&lt;span class=&quot;c&quot;&gt;# PUSH THE HELM CHART TO ACR&lt;/span&gt;
az acr helm push &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; kured-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.tgz

&lt;span class=&quot;c&quot;&gt;# PUSH THE IMAGE TO ACR&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;KURED_CHART_VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;helm show chart kured/kured | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;version |  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
az acr import &lt;span class=&quot;nt&quot;&gt;--source&lt;/span&gt; docker.io/weaveworks/kured:&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;helm show chart kured/kured | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;appVersion |  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ACR_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# NAMESPACE TO USE FOR BASELINE PODS&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/namespace/namespace.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: v1
kind: Namespace
metadata:
  name: baseline
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# KUSTOMIZATION MANIFEST FOR NAMESPACE&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/namespace/kustomization.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: baseline
resources:
  - namespace.yaml
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# CREATE HELMRELEASE MANIFEST FOR KURED&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/kured
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/kured/release.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: kured
spec:
  releaseName: kured
  chart:
    spec:
      chart: kured
      sourceRef:
        kind: HelmRepository
        name: acr-private
        namespace: flux-system
      version: &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KURED_CHART_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
  interval: 1h0m0s
  install:
    remediation:
      retries: 3
  # Default values
  # https://github.com/weaveworks/kured/blob/main/charts/kured/values.yaml
  values:
    tolerations:
    - key: CriticalAddonsOnly
      operator: Exists
    image:
      repository: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ACR_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.azurecr.io/weaveworks/kured
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# KUSTOMIZATION MANIFEST FOR KURED&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;/infrastructure/base/kured/kustomization.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENDOFFILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: baseline
resources:
  - release.yaml
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENDOFFILE

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# COMMIT AND PUSH THE CHANGES&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt;
git add &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;kured installation&quot;&lt;/span&gt;
git push
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;useful-flux-commands&quot;&gt;Useful flux commands&lt;/h1&gt;

&lt;h2 id=&quot;flux-logs&quot;&gt;flux logs&lt;/h2&gt;

&lt;p&gt;The logs command displays formatted logs from various Flux components. Check the usage:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flux logs &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Executing the command without any flags will display all of the flux related logs:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flux logs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
2021-10-17T17:32:11.598Z info GitRepository/flux-system.flux-system - Reconciliation finished in 1.197141019s, next run in 1m0s
2021-10-17T17:32:40.881Z info HelmRepository/acr-private.flux-system - Reconciliation finished in 113.821332ms, next run in 1m0s
2021-10-17T17:33:12.798Z info GitRepository/flux-system.flux-system - Reconciliation finished in 1.200518767s, next run in 1m0s
2021-10-17T17:33:41.017Z info HelmRepository/acr-private.flux-system - Reconciliation finished in 134.590725ms, next run in 1m0s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;flux-reconcile&quot;&gt;flux reconcile&lt;/h2&gt;

&lt;p&gt;The reconcile sub-commands trigger a reconciliation of sources and resources. Check the usage:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flux reconcile &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using the configuration from above, if you want to reconcile the complete deployment, execute:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flux reconcile kustomization infrastructure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;► annotating Kustomization infrastructure in flux-system namespace
✔ Kustomization annotated
◎ waiting for Kustomization reconciliation
✔ applied revision main/2a85d7ec6a63976aaac090197ff0882540a076c0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Azure Kubernetes Service" /><category term="AKS" /><summary type="html">In this series of posts, you will find all the steps needed to build a baseline or reference architecture for Azure Kubernetes Service (AKS) by incorporating all the best practices from the operations and governance perspective. In this post, we will explore the GitOps concept and how to make it work using Flux2.</summary></entry><entry><title type="html">Azure Function keys - what are those and how to access them</title><link href="https://aztoso.com/functions/keys/" rel="alternate" type="text/html" title="Azure Function keys - what are those and how to access them" /><published>2021-10-09T02:00:00+02:00</published><updated>2021-10-09T02:00:00+02:00</updated><id>https://aztoso.com/functions/get-function-key-with-arm</id><content type="html" xml:base="https://aztoso.com/functions/keys/">&lt;h1 id=&quot;usage-of-keys-in-azure-functions&quot;&gt;Usage of keys in Azure Functions&lt;/h1&gt;

&lt;p&gt;Azure Functions offer three levels of authorization:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;anonymous: no key is required&lt;/li&gt;
  &lt;li&gt;function: a function-specific or a host key is required&lt;/li&gt;
  &lt;li&gt;admin: the master key is required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Function access keys are the simple way to protect an Azure Function from unauthorized access. You can provide the access key in the &lt;em&gt;code&lt;/em&gt; query parameter or in the &lt;em&gt;x-functions-key&lt;/em&gt; HTTP header when accessing a function. There are two access scopes for function-level keys:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;function: these keys apply only to the specific functions under which they are defined&lt;/li&gt;
  &lt;li&gt;host: keys with a host scope can be used to access all functions within the function app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The master key also provides access to all functions within the function app and administrative access to the runtime REST APIs. As a best practice, never use the master key in client applications to access an Azure Function.&lt;/p&gt;

&lt;p&gt;One particular type of key, named system key, is used by extensions installed in the Azure Function, such as Event Grid and Durable Functions.&lt;/p&gt;

&lt;h1 id=&quot;how-to-get-the-keys-value&quot;&gt;How to get the keys value?&lt;/h1&gt;

&lt;p&gt;In the examples below, the following applies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;rg-functions-test&lt;/em&gt; - resource group where the function app is deployed&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;aztosotest&lt;/em&gt; - the name of the function app&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Hello&lt;/em&gt; - the name of the function&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;azure-portal&quot;&gt;Azure Portal&lt;/h2&gt;

&lt;p&gt;After opening the Function App in the portal, you can view the host and system keys by selecting &lt;em&gt;App keys&lt;/em&gt; from the left sidebar:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/function/function-host_and_system_keys.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To view the function-specific keys, open the function and chose &lt;em&gt;Function Keys&lt;/em&gt; from the left sidebar:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/function/function-function_level_keys.png&quot; alt=&quot;Desktop View&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;arm-template&quot;&gt;ARM template&lt;/h2&gt;

&lt;p&gt;Note: if the function is deployed in a different resource group, you need to specify the resource group name as a first parameter in the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource#resourceid&quot;&gt;resourceId&lt;/a&gt; function&lt;/p&gt;

&lt;p&gt;Host(default) key:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;listkeys(concat(resourceId('Microsoft.Web/sites',&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'aztosotest'),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'/host/default/'),'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2021-02-01&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;').functionKeys.default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Master key:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;listkeys(concat(resourceId('Microsoft.Web/sites',&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'aztosotest'),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'/host/default/'),'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2021-02-01&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;').masterKey&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;System key (named durabletask_extension):&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;listkeys(concat(resourceId('Microsoft.Web/sites',&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'aztosotest'),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'/host/default/'),'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2021-02-01&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;').systemkeys.durabletask_extension&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Function-specific key:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;listKeys(resourceId('Microsoft.Web/sites/functions',&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'aztosotest',&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'Hello'),'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2021-02-01&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;').default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;azure-cli&quot;&gt;Azure CLI&lt;/h2&gt;

&lt;p&gt;To get the host, master, and system key, use the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az functionapp keys list &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; rg-functions-test &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; aztosotest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;functionKeys&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;68ahyfSAT2PF5HfIuGMyc8Bv6NErsFxnk6lT0l2fYacYCnVameOAmw==&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;masterKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;N4O1a4ng/gLemQEiDFF9HUpNznT/pFGNSnTj4jQXVOhvZAa8MivuOg==&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;systemKeys&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;durabletask_extension&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DtieTxAOV5x76Zfv6NWBqbbW3/u4YiHVhycZfPgn2VIgkcPAGP8kaA==&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following command will list the function-specific keys:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az functionapp &lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;keys list &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; rg-functions-test &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; aztosotest &lt;span class=&quot;nt&quot;&gt;--function-name&lt;/span&gt; Hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ukRsq3yoeayz5ELYgTGK3fbU3yfbHXavy5m9Y6ci0ZSykqFYUxyd3Q==&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;systemData&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Aleksandar Stefanov</name><email>tosokr@gmail.com</email></author><category term="Functions" /><category term="Functions" /><category term="ARM" /><summary type="html">Azure Function Keys are used for authorizing access to the functions. The host and the master key exist at the Function App level, while each function also has a function-specific key that can be used to access that function. You can access the keys from ARM templates, in the portal or using Azure CLI.</summary></entry></feed>