<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Henrique</title>
    <description>The latest articles on DEV Community by Henrique (@hjgraca).</description>
    <link>https://dev.to/hjgraca</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F228912%2F0c995cf4-b9ff-4bec-8935-69251eb6e6f0.png</url>
      <title>DEV Community: Henrique</title>
      <link>https://dev.to/hjgraca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hjgraca"/>
    <language>en</language>
    <item>
      <title>AKS with multiple nginx ingress controllers, Application Gateway and Key Vault certificates</title>
      <dc:creator>Henrique</dc:creator>
      <pubDate>Tue, 01 Dec 2020 19:08:14 +0000</pubDate>
      <link>https://dev.to/hjgraca/aks-with-multiple-nginx-ingress-controllers-application-gateway-and-key-vault-certificates-47b1</link>
      <guid>https://dev.to/hjgraca/aks-with-multiple-nginx-ingress-controllers-application-gateway-and-key-vault-certificates-47b1</guid>
      <description>&lt;h4&gt;
  
  
  Intro
&lt;/h4&gt;

&lt;p&gt;In the  &lt;a href="https://blog.hjgraca.com/aks-ingress-with-nginx-and-key-vault-certificates" rel="noopener noreferrer"&gt;previous blog post&lt;/a&gt;  we created an AKS cluster with an nginx ingress controller and certificates retrieved from Azure Key Vault.&lt;/p&gt;

&lt;p&gt;For this blog post we will extend that previous setup and include;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Deploy Azure Private DNS&lt;/li&gt;
&lt;li&gt;Deploy two nginx ingress controllers running in the cluster (one for internal the other for public traffic. Both with internal ip adresses)&lt;/li&gt;
&lt;li&gt;Deploy one Application Gateway as the entry point for the public traffic, that will be integrated with Key Vault and do SSL termination&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow this blog post it is required that you have an AKS cluster and the components discussed in the  &lt;a href="https://blog.hjgraca.com/aks-ingress-with-nginx-and-key-vault-certificates" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;  installed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  1. Azure Private DNS
&lt;/h4&gt;

&lt;p&gt;Create Azure Private DNS resource&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Setup some variables that we are going to use throughout the scripts
export SUBSCRIPTION_ID="&amp;lt;SubscriptionID&amp;gt;"
export TENANT_ID="&amp;lt;YOUR TENANT ID&amp;gt;"
export RESOURCE_GROUP="&amp;lt;AKSResourceGroup&amp;gt;"
export CLUSTER_NAME="&amp;lt;AKSClusterName&amp;gt;"
export REGION="westeurope"
export NODE_RESOURCE_GROUP="MC_${RESOURCE_GROUP}_${CLUSTER_NAME}_${REGION}"
export IDENTITY_NAME="identity-aad" #must be lower case
export KEYVAULT_NAME="&amp;lt;KeyVault Name&amp;gt;"
export DNS_ZONE="private.contoso.com"
export PUBLIC_DNS_ZONE="public.contoso.com"

az network private-dns zone create -g ${RESOURCE_GROUP} -n ${DNS_ZONE}
az network private-dns zone create -g ${RESOURCE_GROUP} -n ${PUBLIC_DNS_ZONE}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Link the private DNS zone with the AKS Virtual Network  (Vnet that was created by AKS). This will allow for resources in that virtual network to resolve the DNS name. &lt;/p&gt;

&lt;p&gt;You can get the AKS Vnet Resource Id by going into the Node Resource Group that was created by AKS, the naming convention is &lt;code&gt;MC_&amp;lt;resource group name&amp;gt;_&amp;lt;aks name&amp;gt;_&amp;lt;region&amp;gt;&lt;/code&gt;. Click the Virtual Network resource and copy the Virtual Network Resource Id from the properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az network private-dns link vnet create -g ${RESOURCE_GROUP} -n MyDNSLink -z ${DNS_ZONE} -v &amp;lt;AKS Vnet Resource ID&amp;gt; -e true

#register public dns zone - this time auto register is false because only one link can be true in the same vnet
az network private-dns link vnet create -g ${RESOURCE_GROUP} -n MyDNSLink -z ${PUBLIC_DNS_ZONE} -v &amp;lt;AKS Vnet Resource ID&amp;gt; -e false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When finished you can check the link was created in the Private DNS Zone&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606753209251%2Ft4JwFCuaQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606753209251%2Ft4JwFCuaQ.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. nginx ingress controller
&lt;/h4&gt;

&lt;p&gt;First let's create a new certificate and import it to Key Vault. The other certificate for the DNS_ZONE was created in the  &lt;a href="https://blog.hjgraca.com/aks-ingress-with-nginx-and-key-vault-certificates" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export CERT_NAME=publicingresscert
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -out ingress-tls.crt \
    -keyout ingress-tls.key \
    -subj "/CN=${PUBLIC_DNS_ZONE}"

openssl pkcs12 -export -in ingress-tls.crt -inkey ingress-tls.key  -out $CERT_NAME.pfx
# skip Password prompt

az keyvault certificate import --vault-name ${KEYVAULT_NAME} -n $CERT_NAME -f $CERT_NAME.pfx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If continuing from the  &lt;a href="https://blog.hjgraca.com/aks-ingress-with-nginx-and-key-vault-certificates" rel="noopener noreferrer"&gt;last post&lt;/a&gt;  you should have two certificates, with the addition of the newly created &lt;code&gt;publicingresscert&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606770409223%2Fc1W9GF3si.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606770409223%2Fc1W9GF3si.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this article I wont be creating a new identities and will reuse the same identity created in  &lt;a href="https://blog.hjgraca.com/aks-ingress-with-nginx-and-key-vault-certificates" rel="noopener noreferrer"&gt;the last post&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now lets configure the ingress controllers. To be able to run multiple ingress controllers you need to configure the &lt;strong&gt;ingress class&lt;/strong&gt; and add the correct annotation to the ingress to map to the correct ingress controller.  &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/#multiple-ingress-nginx-controllers" rel="noopener noreferrer"&gt;More info&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Install or upgrade the helm release (if continuing from the previous post)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# internal ingress controller - set ingress class = nginx-internal
helm install nginx-ingress-internal ingress-nginx/ingress-nginx \
    --set controller.replicaCount=2 \
    --set controller.ingressClass=nginx-internal \
    --set controller.podLabels.aadpodidbinding=${IDENTITY_NAME} \
    -f - &amp;lt;&amp;lt;EOF
controller:
  service:
    annotations:
      service.beta.kubernetes.io/azure-load-balancer-internal: "true"
  extraVolumes:
      - name: secrets-store-inline
        csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "ingress-tls"   #name of the SecretProviderClass we created above
  extraVolumeMounts:
      - name: secrets-store-inline
        mountPath: "/mnt/secrets-store"
        readOnly: true
EOF

#external ingress controller - set ingress class = nginx-external
helm install nginx-ingress-external ingress-nginx/ingress-nginx \
    --set controller.replicaCount=2 \
    --set controller.ingressClass=nginx-external \
    --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the scripts each ingress controller has an &lt;code&gt;ingressClass&lt;/code&gt; and the annotation for internal load balancer defined.&lt;/p&gt;

&lt;p&gt;After the install succeeds you will have 4 new pods, two for the internal ingress and two for the external ingress because we defined 2 replicas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606825438205%2F0xP8LA2qn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606825438205%2F0xP8LA2qn.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are also 2 new services with internal ips from the range of your virtual network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606825479612%2FO_ZL559YI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606825479612%2FO_ZL559YI.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now lets created both ingresses, remember the &lt;strong&gt;ingress class&lt;/strong&gt; we defined for each ingress controller? Now is the time to use that property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#deploy the second demo app
kubectl apply -f https://raw.githubusercontent.com/hjgraca/playground/master/k8s/nginx/secondapp.yaml

#internal ingress
cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx-internal
  name: internal-ingress
  namespace: default
spec:
  tls:
  - hosts:
    - $DNS_ZONE
    secretName: ingress-tls-csi   # the secret that got created by the secret store provider and assigned to the ingress controller
  rules:
    - host: $DNS_ZONE
      http:
        paths:
          - backend:
              serviceName: firstapp
              servicePort: 80
            path: /
EOF

#external ingress, without TLS, the TLS is taken care by Application Gateway
cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx-external
  name: external-ingress
  namespace: default
spec:
  rules:
    - host: $PUBLIC_DNS_ZONE
      http:
        paths:
          - backend:
              serviceName: secondapp
              servicePort: 80
            path: /
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed we can get the ingresses, the list will show the hosts and the binding ip, if you describe one of the ingresses you can see what service and pods it is attached to.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I found that its better to wait for one ingress controller to be fully installed and only then move to the next one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606826056353%2FFuHcps2C2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606826056353%2FFuHcps2C2.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Let's test the endpoints
&lt;/h4&gt;

&lt;p&gt;Firstly lets add those ip addresses to the Private DNS Zones we created earlier&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az network private-dns record-set a add-record -g ${RESOURCE_GROUP} -z ${DNS_ZONE} -n @ -a &amp;lt;ip address of internal ingress&amp;gt;

az network private-dns record-set a add-record -g ${RESOURCE_GROUP} -z ${PUBLIC_DNS_ZONE} -n @ -a &amp;lt;ip address of external ingress&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those scripts will create A records for the domain in the Private DNS Zones&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606827079882%2FLD2T2-J0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606827079882%2FLD2T2-J0w.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we are using internal ip addresses, the only way we can resolve those ips is from within that virtual network, vpn, bastion. I am going for the simplest option of running a pod inside the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#run a pod with a tweaked busybox image with curl
kubectl run -it --rm jumpbox-pod --image=radial/busyboxplus:curl

#when in the prompt we can now curl our private endpoints
#public -&amp;gt; external ingress -&amp;gt; non TLS -&amp;gt; returns secondapp
curl http://public.contoso.com

#private -&amp;gt; internal ingress -&amp;gt; TLS -&amp;gt; returns firstapp
curl -v -k https://private.contoso.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now internal consumers can get to our TLS protected endpoint using &lt;code&gt;https://private.contoso.com&lt;/code&gt; as long as they are in the same virtual network, for other ways of resolution, like resolving from on-premises you will need a DNS forwarder running in Azure that will forward the DNS queries to the Azure DNS.&lt;/p&gt;

&lt;p&gt;As for external consumers we want to protected that endpoint using &lt;strong&gt;Application Gateway&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Azure Application Gateway to expose service
&lt;/h4&gt;

&lt;p&gt;To create an Application Gateway we first need some infrastructure to be present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#first create a subnet in the existing AKS Vnet
az network vnet subnet create \
  --name AppGwSubnet \
  --resource-group ${NODE_RESOURCE_GROUP} \ 
  --vnet-name &amp;lt;name of the vnet&amp;gt;  \
  --address-prefix 10.0.2.0/27

#Now lets create the public ip that will serve the public requests
az network public-ip create \
  --resource-group ${RESOURCE_GROUP} \
  --name myAGPublicIPAddress \
  --allocation-method Static \
  --sku Standard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results in 2 subnets&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606829136658%2F6Qs0FpZRA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606829136658%2F6Qs0FpZRA.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And a Public Ip Address in your Resource Group&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606829171904%2FpWwOBTiEX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606829171904%2FpWwOBTiEX.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's create the Application Gateway (&lt;strong&gt;disclaimer&lt;/strong&gt;; since I did not create a vnet in the resource group where the AKS is I will have to deploy to the Node Resource Group)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export APPGW_NAME="myAppGateway"

#create application gateway
az network application-gateway create \
  --name ${APPGW_NAME} \
  --location ${REGION} \
  --resource-group ${NODE_RESOURCE_GROUP} \
  --capacity 1 \
  --sku Standard_v2 \
  --public-ip-address myAGPublicIPAddress \
  --vnet-name &amp;lt;VNET Name&amp;gt; \
  --subnet AppGwSubnet

#add backend pool ip address of our ingress
az network application-gateway address-pool create -g ${NODE_RESOURCE_GROUP} --gateway-name ${APPGW_NAME} -n AKSAddressPool --servers &amp;lt;ip address of your public ingress&amp;gt;

# One time operation, assign the identity to Application Gateway so we can access key vault
az network application-gateway identity assign \
  --gateway-name ${APPGW_NAME} \
  --resource-group ${NODE_RESOURCE_GROUP} \
  --identity ${IDENTITY_RESOURCE_ID}

#get secret-id of your certificate from keyvault
versionedSecretId=$(az keyvault certificate show -n publicingresscert --vault-name $KEYVAULT_NAME --query "sid" -o tsv)
unversionedSecretId=$(echo $versionedSecretId | cut -d'/' -f-5) # remove the version from the url

#add the ssl certificate to app gateway
az network application-gateway ssl-cert create \
  --resource-group ${NODE_RESOURCE_GROUP} \
  --gateway-name ${APPGW_NAME} \
  -n MySSLCert \
  --key-vault-secret-id $unversionedSecretId

#create a new port for HTTPS
az network application-gateway frontend-port create \
  --port 443 \
  --gateway-name ${APPGW_NAME} \
  --resource-group ${NODE_RESOURCE_GROUP} \
  --name port443

#create an HTTP-LISTENER for HTTPS that uses your certificate
#this is your entry point
az network application-gateway http-listener create -g ${NODE_RESOURCE_GROUP} --gateway-name ${APPGW_NAME} \
    --frontend-port port443 -n https --frontend-ip appGatewayFrontendIP  --ssl-cert MySSLCert --host-name ${PUBLIC_DNS_ZONE}

#create a rule to map the http listener to the backend pool
az network application-gateway rule create \
  --gateway-name ${APPGW_NAME} \
  --name rule2 \
  --resource-group ${NODE_RESOURCE_GROUP} \
  --http-listener https \
  --address-pool AKSAddressPool

# create a custom probe with the host name specified
az network application-gateway probe create -g ${NODE_RESOURCE_GROUP} --gateway-name ${APPGW_NAME} \
    -n MyProbe --protocol http --host ${PUBLIC_DNS_ZONE} --path /


# upadte the http-settings to use the new probe
az network application-gateway http-settings update --enable-probe true --gateway-name ${APPGW_NAME} --name appGatewayBackendHttpSettings --probe MyProbe --resource-group ${NODE_RESOURCE_GROUP}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deploying and configuring the Application Gateway we can now test our public endpoint, since this is not a domain I own we will need to use curl or change the local hosts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#curl the public endpoint resolve the ip and validate the certificates
curl -v -k --resolve $PUBLIC_DNS_ZONE:443:20..***** https://$PUBLIC_DNS_ZONE

#the response 
* Server certificate:
*  subject: CN=public.contoso.com
*  start date: Nov 30 21:05:33 2020 GMT
*  expire date: Nov 30 21:05:33 2021 GMT
*  issuer: CN=public.contoso.com
*  SSL certificate verify result: self signed certificate (18), continuing anyway.

#and the secondapp html is returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  We did it!
&lt;/h4&gt;

&lt;p&gt;What did we do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployed two ingress controllers in our cluster, one for internal traffic the other for public traffic; the scenario is when you want to separate your workloads for different audiances&lt;/li&gt;
&lt;li&gt;Deployed one ingress with TLS, the certificate was stored in Key Vault and we were able to assign that secret to our ingress using  &lt;a href="https://github.com/Azure/aad-pod-identity" rel="noopener noreferrer"&gt;AAD pod identity&lt;/a&gt;  and  &lt;a href="https://github.com/Azure/secrets-store-csi-driver-provider-azure" rel="noopener noreferrer"&gt;CSI Secret Store Provider&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deployed another ingress without TLS but still internal load balancer&lt;/li&gt;
&lt;li&gt;Deployed an Azure Application Gateway that does the TLS offloading and gets the certificate from Key Vault, it uses the same User Assigned Managed Identity that we used for the AAD pod identity to access Key Vault&lt;/li&gt;
&lt;li&gt;A private DNS zone so we can resolve DNS names from our virtual network&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Final Notes
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;It's easier/recommended to deploy Application Gateway using ARM or Terraform&lt;/li&gt;
&lt;li&gt;If you want to automatically add your private DNS Zones based on the Kubernetes ingresses you can use a tool like  &lt;a href="https://github.com/kubernetes-sigs/external-dns" rel="noopener noreferrer"&gt;External-DNS&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aks</category>
      <category>keyvault</category>
      <category>nginx</category>
      <category>azure</category>
    </item>
    <item>
      <title>AKS ingress with nginx and Key Vault certificates</title>
      <dc:creator>Henrique</dc:creator>
      <pubDate>Mon, 30 Nov 2020 20:14:39 +0000</pubDate>
      <link>https://dev.to/hjgraca/aks-ingress-with-nginx-and-key-vault-certificates-2kma</link>
      <guid>https://dev.to/hjgraca/aks-ingress-with-nginx-and-key-vault-certificates-2kma</guid>
      <description>&lt;h4&gt;
  
  
  Intro
&lt;/h4&gt;

&lt;p&gt;In this blog post I will show you how to deploy nginx ingress controller in AKS and secure the backends with TLS certificates that are stored in Key Vault. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt; of this blog post can be found &lt;a href="https://blog.hjgraca.com/aks-with-multiple-nginx-ingress-controllers-application-gateway-and-key-vault-certificates" rel="noopener noreferrer"&gt;here&lt;/a&gt;, were we augment this setup with internal ip addresses and Azure Application Gateway &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To securely access Key Vault from the pods we will create an User Assigned Managed Identity and install  &lt;a href="https://github.com/Azure/aad-pod-identity" rel="noopener noreferrer"&gt;AAD pod identity&lt;/a&gt;  and  &lt;a href="https://github.com/Azure/secrets-store-csi-driver-provider-azure" rel="noopener noreferrer"&gt;CSI Secret Store Provider&lt;/a&gt;  in the cluster.&lt;/p&gt;

&lt;h4&gt;
  
  
  What we will build
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/azure/aks/intro-kubernetes" rel="noopener noreferrer"&gt;Azure Kubernetes Service&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="https://docs.microsoft.com/en-us/azure/key-vault/general/overview" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Roles&lt;/li&gt;
&lt;li&gt;Deploy  &lt;a href="https://github.com/Azure/aad-pod-identity" rel="noopener noreferrer"&gt;AAD pod Identity&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Deploy  &lt;a href="https://github.com/Azure/secrets-store-csi-driver-provider-azure" rel="noopener noreferrer"&gt;CSI Secret Store Provider&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="https://kubernetes.github.io/ingress-nginx/" rel="noopener noreferrer"&gt;nginx ingress controller&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Preparing the ingress&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  1. Installing AKS
&lt;/h4&gt;

&lt;p&gt;The simplest way to create and AKS cluster is using Azure command line interface. For instructions on how to install follow  &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After installing Azure cli it is also convinient to install kubectl to access the cluster, you can do that running &lt;code&gt;az aks install-cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First create a resource group and create the AKS cluster with the default settings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With default settings you will create a cluster with Kubenet networking. This is enough for this blog post.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Setup some variables that we are going to use throughout the scripts
export SUBSCRIPTION_ID="&amp;lt;SubscriptionID&amp;gt;"
export TENANT_ID="&amp;lt;YOUR TENANT ID&amp;gt;"
export RESOURCE_GROUP="&amp;lt;AKSResourceGroup&amp;gt;"
export CLUSTER_NAME="&amp;lt;AKSClusterName&amp;gt;"
export REGION="westeurope"
export NODE_RESOURCE_GROUP="MC_${RESOURCE_GROUP}_${CLUSTER_NAME}_${REGION}"
export IDENTITY_NAME="identity-aad" #must be lower case
export KEYVAULT_NAME="&amp;lt;KeyVault Name&amp;gt;"
export DNS_ZONE="private.contoso.com"

#create resource group
az group create --name ${RESOURCE_GROUP} --location ${REGION}
#create cluster
az aks create --resource-group ${RESOURCE_GROUP} --name ${CLUSTER_NAME} --node-count 1 --generate-ssh-keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Azure Key Vault
&lt;/h4&gt;

&lt;p&gt;Same as before we will use Azure cli to install Key Vault on the same resource group as AKS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az keyvault create --name ${KEYVAULT_NAME} --resource-group ${RESOURCE_GROUP} --location ${REGION}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Roles
&lt;/h4&gt;

&lt;p&gt;Remember when AKS was able to create Azure resources this was because AKS has an identity, in our case a Service Principal that has permissions to manage resources on the Node Resource Group. &lt;/p&gt;

&lt;p&gt;This section explains various role assignments that need to be performed before using AAD Pod Identity. Without the proper role assignments, your Azure cluster will not have the correct permission to assign and un-assign identities from the underlying virtual machines (VM) or virtual machine scale sets (VMSS).&lt;/p&gt;

&lt;p&gt;To follow best practices I will not be using the AKS Service Principal but will create a User Assigned Managed Identity and bind it to AAD Pod Identity.&lt;/p&gt;

&lt;p&gt;We will also provide this managed identity access to get certificates from Key Vault&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Get the AKS service principal Id
export SP_ID="$(az aks show -g ${RESOURCE_GROUP} -n ${CLUSTER_NAME} --query servicePrincipalProfile.clientId -otsv)"
#Assign the roles
az role assignment create --role "Managed Identity Operator" --assignee ${SP_ID} --scope /subscriptions/${SUBSCRIPTION_ID}/resourcegroups/${NODE_RESOURCE_GROUP}
az role assignment create --role "Virtual Machine Contributor" --assignee ${SP_ID} --scope /subscriptions/${SUBSCRIPTION_ID}/resourcegroups/${NODE_RESOURCE_GROUP}

#Create an User Assigned Managed Identity
az identity create -g ${RESOURCE_GROUP} -n ${IDENTITY_NAME}
export IDENTITY_CLIENT_ID="$(az identity show -g ${RESOURCE_GROUP} -n ${IDENTITY_NAME} --query clientId -otsv)"
export IDENTITY_RESOURCE_ID="$(az identity show -g ${RESOURCE_GROUP} -n ${IDENTITY_NAME} --query id -otsv)"

#Assign the Service principal as an operator to the Managed User Identity
az role assignment create --role "Managed Identity Operator" --assignee ${SP_ID} --scope ${IDENTITY_RESOURCE_ID}

#Assign Reader Role to new Identity for your keyvault
az role assignment create --role Reader --assignee ${IDENTITY_CLIENT_ID} --scope /subscriptions/${SUBSCRIPTION_ID}/resourcegroups/${RESOURCE_GROUP}/providers/Microsoft.KeyVault/vaults/${KEYVAULT_NAME}

# set policy to access certs in your keyvault
az keyvault set-policy -n ${KEYVAULT_NAME} --certificate-permissions get --spn ${IDENTITY_CLIENT_ID}
az keyvault set-policy -n ${KEYVAULT_NAME} --secret-permissions get --spn ${IDENTITY_CLIENT_ID}

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For next steps we will require &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helm to be installed,  &lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;instructions here&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Kubernetes context correctly configured; &lt;code&gt;az aks get-credentials -g ${RESOURCE_GROUP} -n ${CLUSTER_NAME}&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  4. AAD pod identity
&lt;/h4&gt;

&lt;p&gt;Now that we have our roles configured it's time to install AAD Pod Identity, which enables Kubernetes applications to access cloud resources securely with Azure Active Directory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since we are using kubenet and starting on version 1.7 of AAD pod identity we need to explicitly enable the support for it to be able to run in Kubenet clusters by enabling the flag &lt;code&gt;--allow-network-plugin-kubenet=true&lt;/code&gt;.  &lt;a href="https://azure.github.io/aad-pod-identity/docs/configure/aad_pod_identity_on_kubenet/" rel="noopener noreferrer"&gt;More info&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#add the helm repo to our helm workspace
helm repo add aad-pod-identity https://raw.githubusercontent.com/Azure/aad-pod-identity/master/charts
#install aad pod identity with the Kubenet flag
helm install aad-pod-identity aad-pod-identity/aad-pod-identity --set nmi.allowNetworkPluginKubenet=true

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

&lt;/div&gt;



&lt;p&gt;Now that the crds are installed and the AAD pod identity pods are running we can deploy our identity and identity binding.&lt;/p&gt;

&lt;p&gt;Create an AzureIdentity with &lt;strong&gt;type 0&lt;/strong&gt; (Set type: 0 for user-assigned MSI, type: 1 for Service Principal with client secret, or type: 2 for Service Principal with certificate. For more information, see  &lt;a href="https://azure.github.io/aad-pod-identity/docs/concepts/azureidentity/" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentity
metadata:
  name: ${IDENTITY_NAME}
spec:
  type: 0
  resourceID: ${IDENTITY_RESOURCE_ID} #Resource Id of the Managed Identity
  clientID: ${IDENTITY_CLIENT_ID} #Id of the Managed Identity 
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the Identity is created we need to create an &lt;strong&gt;identity binding&lt;/strong&gt; that defines the relationship between an AzureIdentity and a pod with a specific selector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentityBinding
metadata:
  name: ${IDENTITY_NAME}-binding
spec:
  azureIdentity: ${IDENTITY_NAME}
  selector: ${IDENTITY_NAME}
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. CSI Secret Store Provider (Key Vault integration)
&lt;/h4&gt;

&lt;p&gt;Firstly lets create a certificate and store it in Key Vault&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export CERT_NAME=ingresscert
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -out ingress-tls.crt \
    -keyout ingress-tls.key \
    -subj "/CN=${DNS_ZONE}"

openssl pkcs12 -export -in ingress-tls.crt -inkey ingress-tls.key  -out $CERT_NAME.pfx
# skip Password prompt

az keyvault certificate import --vault-name ${KEYVAULT_NAME} -n $CERT_NAME -f $CERT_NAME.pfx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a new certificate in the Certificates window in Key Vault&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606758989762%2F1gdf0Zsga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606758989762%2F1gdf0Zsga.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's install the Secrets Store Provider&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#add the repo
helm repo add csi-secrets-store-provider-azure https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/charts
#install chart
helm install csi-secrets-store-provider-azure/csi-secrets-store-provider-azure --generate-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have another crd available &lt;code&gt;SecretProviderClass&lt;/code&gt;, this crd provides provider-specific (Azure) parameters to the Secret Score CSI driver.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: The SecretProviderClass has to be in the same namespace as the pod referencing it.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -n default -f -
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
  name: ingress-tls
spec:
  provider: azure
  secretObjects:                                
  - secretName: ingress-tls-csi    #name of the secret that gets created - this is the value we provide to nginx
    type: kubernetes.io/tls
    data: 
    - objectName: $CERT_NAME
      key: tls.key
    - objectName: $CERT_NAME
      key: tls.crt
  parameters:
    usePodIdentity: "true"   #since we are using aad pod identity we set this to true
    keyvaultName: $KEYVAULT_NAME                        
    objects: |
      array:
        - |
          objectName: $CERT_NAME
          objectType: secret
    tenantId: $TENANT_ID      # the tenant ID of KeyVault            
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Install nginx ingress controller
&lt;/h4&gt;

&lt;p&gt;We are now going to create an ingress controller that will expose a public ip and will be able to serve TLS requests (It will give a warning because its a self-signed certificate)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#add the repo
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
#install nginx ingress controller
helm install ingress-nginx/ingress-nginx --generate-name \
    --set controller.replicaCount=2 \
    --set controller.podLabels.aadpodidbinding=${IDENTITY_NAME} \
    -f - &amp;lt;&amp;lt;EOF
controller:
  extraVolumes:
      - name: secrets-store-inline
        csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "ingress-tls"   #name of the SecretProviderClass we created above
  extraVolumeMounts:
      - name: secrets-store-inline
        mountPath: "/mnt/secrets-store"
        readOnly: true
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will install nginx ingress controller and configure the Secret Store driver. &lt;br&gt;
We also set the label &lt;code&gt;aadpodidbinding&lt;/code&gt; with the identity name for the identity binding to assign the correct identity to this pod.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;it's also recommended to have at least 2 replicas of the ingress running. When you need to refresh the certificate the way you have to do it is by restarting the ingress controller pods. There is some work happening that will improve this by providing a better way of refreshing secrets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the moment of truth if everything worked as expected this is when nginx ingress controller will ask Secret Store driver for a secret, and in turn Secret Store will use AAD pod identity to get that secret from Key Vault&lt;/p&gt;

&lt;p&gt;The list of running pods&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606763254125%2F6QyUkTCCc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606763254125%2F6QyUkTCCc.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The list of secrets which contain the secret that was created by the Secret Store provider &lt;code&gt;ingress-tls-csi&lt;/code&gt;, this is the secret we will use on nginx ingress&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606763310673%2FQePkd_VLd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606763310673%2FQePkd_VLd.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  7. Preparing the ingress
&lt;/h4&gt;

&lt;p&gt;Let's start by deploying a simple demo application, that exposes a ClusterIP service on port 80 and will serve as the backend to our ingress&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/hjgraca/playground/master/k8s/nginx/firstapp.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's deploy the ingress&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress
  namespace: default
spec:
  tls:
  - hosts:
    - $DNS_ZONE
    secretName: ingress-tls-csi
  rules:
    - host: $DNS_ZONE
      http:
        paths:
          - backend:
              serviceName: firstapp
              servicePort: 80
            path: /
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606764727632%2FGPcr-P92L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606764727632%2FGPcr-P92L.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606764045194%2F6o1AXosOU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606764045194%2F6o1AXosOU.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's access that public ip to see if everything worked. &lt;br&gt;
Since private.contoso.com is not a valid domain owned by us and mapped to that ip address to be able to get a response we either change our local hosts file or use &lt;code&gt;curl&lt;/code&gt; to do that for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v -k --resolve $DNS_ZONE:443:20.71.67.138 https://$DNS_ZONE

#you should see something like this in the reply
* Server certificate:
*  subject: CN=private.contoso.com
*  start date: Nov 30 17:47:25 2020 GMT
*  expire date: Nov 30 17:47:25 2021 GMT
*  issuer: CN=private.contoso.com
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if I change my hosts file, I can resolve the domain private.contoso.com but I get an expected error from the browser, if I check the certificate I can see it is indeed my self-signed certificate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606765715335%2FJMbfiDu20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606765715335%2FJMbfiDu20.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it you now have a secure(ish) TLS endpoint using a certificate stored in Key Vault.&lt;/p&gt;

&lt;h4&gt;
  
  
  Final notes
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Delete the ingress controller
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm delete ingress-nginx-****
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will delete the public ip address and the secret that contains the certificate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you are looking into storing &lt;strong&gt;Let's Encrypt&lt;/strong&gt; certificates in Key Vault have a look at  &lt;a href="https://github.com/shibayan/appservice-acmebot" rel="noopener noreferrer"&gt;this project on GitHub&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;If you want to renew the certificate, after updating it in Key Vault you will need to delete the existing ingress controller pods, so they can ask for a new secret to be generated.&lt;/li&gt;
&lt;li&gt;For the second part of this tutorial I will add a Private DNS zone and two ingress controllers one for public and the other for private traffic.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aks</category>
      <category>nginx</category>
      <category>kubernetes</category>
      <category>keyvault</category>
    </item>
    <item>
      <title>Style your Windows terminal and use WSL and PowerShell like a pro</title>
      <dc:creator>Henrique</dc:creator>
      <pubDate>Thu, 14 Nov 2019 11:12:11 +0000</pubDate>
      <link>https://dev.to/hjgraca/style-your-windows-terminal-and-use-wsl-and-powershell-like-a-pro-57fp</link>
      <guid>https://dev.to/hjgraca/style-your-windows-terminal-and-use-wsl-and-powershell-like-a-pro-57fp</guid>
      <description>&lt;p&gt;With zsh and powerline10k you can have a terminal that will make your unix friends jealous 😀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BXFFrmXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3zKJWYBLG3G9IJsonITdpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BXFFrmXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3zKJWYBLG3G9IJsonITdpg.png" alt="" width="880" height="461"&gt;&lt;/a&gt;Finished look of the terminal&lt;/p&gt;

&lt;h3&gt;
  
  
  Some assumptions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Have &lt;strong&gt;WSL&lt;/strong&gt; installed. If not you can follow the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10"&gt;&lt;strong&gt;official docs&lt;/strong&gt;&lt;/a&gt; (I am using Ubuntu 18.04). Once you finished installing you need to setup for the first time, follow tutorial &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/initialize-distro"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(optional) Have &lt;strong&gt;WSL 2&lt;/strong&gt; installed following the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/wsl2-install"&gt;&lt;strong&gt;official docs&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(&lt;/strong&gt;WSL 2 is only available in Windows 10 builds 18917 or higher)&lt;/li&gt;
&lt;li&gt;Have the new &lt;strong&gt;Windows Terminal&lt;/strong&gt; (preview) installed. You can get it from the &lt;a href="https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701"&gt;&lt;strong&gt;Windows Store&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Without any styling you should have something very similar to this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xhJsNg3X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AjQr-2NhKrP0KxLm_noAUfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xhJsNg3X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AjQr-2NhKrP0KxLm_noAUfg.png" alt="" width="880" height="510"&gt;&lt;/a&gt;Default Windows Terminal screen&lt;/p&gt;

&lt;p&gt;The first thing I recommend it to go into the settings and change the default profile to Ubuntu. After you click settings your json editor will popup with the profiles.json file.&lt;/p&gt;

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

&lt;p&gt;Next you are going to add two new schemes to the file and change the default values from the PowerShell and Ubuntu profiles. The end result will be:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Great after we saved the file we should see something different&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1dXPZcp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AROBh9exlXIhDZOKJUJf-ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1dXPZcp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AROBh9exlXIhDZOKJUJf-ag.png" alt="" width="880" height="478"&gt;&lt;/a&gt;Ubuntu&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u6Ulecnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A6d1TZiH5cpyxYRfq_wEReQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u6Ulecnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A6d1TZiH5cpyxYRfq_wEReQ.png" alt="" width="880" height="479"&gt;&lt;/a&gt;PowerShell&lt;/p&gt;

&lt;p&gt;Looks better but not there yet. You must have noticed the font faces in the profiles, those do not exist in windows by default so we need to install them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fonts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Install Powerline fonts in Windows&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
To have a correctly working theme, you needed to download and install some needed fonts.&lt;/p&gt;

&lt;p&gt;Open Powershell command prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone [https://github.com/powerline/fonts.git](https://github.com/powerline/fonts.git) --depth=1

cd fonts

.\install.ps1

# this will take a while to install all fonts. you can delete the folder after.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next go to &lt;a href="https://www.nerdfonts.com/"&gt;&lt;strong&gt;Nerd Fonts&lt;/strong&gt;&lt;/a&gt; and download and install the font you like. In this tutorial I downloaded the &lt;a href="https://github.com/ryanoasis/nerd-fonts/releases/download/v2.0.0/Mononoki.zip"&gt;&lt;strong&gt;mononoki&lt;/strong&gt;&lt;/a&gt;font. These are custom fonts and icons for your terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--61tRL-Ob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A8nx5Eoaexv-CRNngxGQxPA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--61tRL-Ob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A8nx5Eoaexv-CRNngxGQxPA.png" alt="" width="880" height="460"&gt;&lt;/a&gt;&lt;strong&gt;Unzip the file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DlVeXmLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AqdpZgR2BPT4i_yDByBJ8rA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DlVeXmLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AqdpZgR2BPT4i_yDByBJ8rA.png" alt="" width="880" height="468"&gt;&lt;/a&gt;&lt;strong&gt;And install the font&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  PowerShell
&lt;/h3&gt;

&lt;p&gt;Let´s start by styling PowerShell (scroll down if you want to see WSL). You will need to have &lt;a href="https://git-scm.com/downloads"&gt;&lt;strong&gt;installed Git for Windows&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Follow &lt;a href="https://github.com/JanDeDobbeleer/oh-my-posh?WT.mc_id=-blog-scottha#installation"&gt;&lt;strong&gt;these directions&lt;/strong&gt;&lt;/a&gt;, this will install Posh-Git and Oh-My-Posh.&lt;/p&gt;

&lt;p&gt;Install posh-git and oh-my-posh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# You could have the following to allow for scripts execution 
# Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Install-Module posh-git -Scope CurrentUser
Install-Module oh-my-posh -Scope CurrentUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Start the default settings (might not work so optional)
Set-Prompt

# To enable the engine edit your PowerShell profile, run
notepad $PROFILE

# and append the following lines to the profile file you just opened (or created in case the file was not there already):

Import-Module posh-git
Import-Module oh-my-posh
Set-Theme Paradox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you are done this is what your PowerShell will look like. You can get other themes &lt;a href="https://github.com/JanDeDobbeleer/oh-my-posh?WT.mc_id=-blog-scottha#themes"&gt;here&lt;/a&gt;. And as you can see all icons are there including the git icons 😎&lt;/p&gt;

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

&lt;h3&gt;
  
  
  WSL
&lt;/h3&gt;

&lt;p&gt;Now that we have PowerShell styled lets take care of Ubuntu.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First install zsh
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install zsh curl git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://github.com/robbyrussell/oh-my-zsh"&gt;oh-my-zsh&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# When prompted to set zsh as your default shell say Yes

sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all went well, your Ubuntu terminal shell should look like this. Just your username in the prompt.&lt;/p&gt;

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

&lt;p&gt;Now we are ready to add some themes. I will be using &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;&lt;strong&gt;Powerlevel10k&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;.&lt;/strong&gt; And because we have &lt;strong&gt;oh my zsh&lt;/strong&gt; installed we just need to run the following scripts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone --depth=1 https://github.com/romkatv/powerlevel10k.git $ZSH\_CUSTOM/themes/powerlevel10k
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set ZSH_THEME=powerlevel10k/powerlevel10k in your ~/.zshrc. To do that run vim ~/.zshrcand replace the &lt;strong&gt;ZSH_THEME&lt;/strong&gt; value. Save and exit Vim 😂&lt;/p&gt;

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

&lt;p&gt;Now restart the Windows Terminal and you should see the Powerlevel10k configuration wizard&lt;/p&gt;

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

&lt;p&gt;If all the fonts were installed correctly you should see a diamond (like above) a lock and a debian logo. Follow the wizard until the end and then apply your changes to the ~/.zshrc file&lt;/p&gt;

&lt;p&gt;Depending on the style you chose you should have a good looking terminal like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJluYtnj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AV826s1-NRtU6dfhwz1tQYg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJluYtnj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AV826s1-NRtU6dfhwz1tQYg.png" alt="" width="880" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it you now have a very good looking PowerShell and WSL terminal that will make your unix friends jealous 😍&lt;/p&gt;

&lt;h3&gt;
  
  
  Extras for WSL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;zsh and Powerlevel10k have an enormous amount of plugins and extensions that you can add to your shell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add Auto Suggestions (&lt;/strong&gt;&lt;a href="https://github.com/zsh-users/zsh-autosuggestions"&gt;&lt;strong&gt;repo&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repository into $ZSH_CUSTOM/plugins (by default ~/.oh-my-zsh/custom/plugins)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone [https://github.com/zsh-users/zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) ${ZSH\_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the plugin to the list of plugins for Oh My Zsh to load (inside ~/.zshrc): (you will probably have git already)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins=(zsh-autosuggestions)

# and bellow add: to fix the suggestion style

ZSH\_AUTOSUGGEST\_HIGHLIGHT\_STYLE="fg=244"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start a new terminal session. And now you have auto suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Kubectl plugin (&lt;/strong&gt;&lt;a href="https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/kubectl"&gt;&lt;strong&gt;repo&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For those like me that have to deal with Kubernetes this is an amazing plugin, because it gives you autocomplete and many aliases for you to have to type less. What do you have to do?! Very easy just open ~/.zshrc and edit the plugins line and add kubectl. Save and exit Vim&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins=(git zsh-autosuggestions **kubectl** )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the terminal session&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Styling the prompt with Powerlevel10k&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can add many things to the prompt, like aws or azure login user information, Kubernetes cluster and namespace information, nodejs version and so on. To do so we need to edit the Powerlevel10k configuration file &lt;strong&gt;~/.p10k.zsh&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim ~/.p10k.zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the POWERLEVEL9K_LEFT_PROMPT_ELEMENTS to change the left hand side of the prompt.&lt;/p&gt;

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

&lt;p&gt;Change the POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS to change the right hand side of the prompt. This is where most of the elements are.&lt;/p&gt;

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

&lt;p&gt;For example to add the Kubernetes cluster and namespace information I just enable the &lt;strong&gt;kubecontext&lt;/strong&gt; element. And for the battery information the *&lt;em&gt;battery *&lt;/em&gt; element&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---_3YOz2S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AyWP-hVtYoY4nJFkyITW0kg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---_3YOz2S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AyWP-hVtYoY4nJFkyITW0kg.png" alt="" width="880" height="461"&gt;&lt;/a&gt;In pink the Kubernetes cluster(aks9533) and namepsace (demo).&lt;/p&gt;

&lt;p&gt;And that’s it, totally customizable and good looking terminal. To learn more have a look at the &lt;a href="https://github.com/Powerlevel9k/powerlevel9k/wiki/Stylizing-Your-Prompt"&gt;style your prompt guide&lt;/a&gt; of Powerlevel9k (original project) to know what to change.&lt;/p&gt;

</description>
      <category>windowsterminal</category>
      <category>zsh</category>
      <category>wsl</category>
      <category>ohmyzsh</category>
    </item>
    <item>
      <title>Using WSL2 + Visual Studio Code for Jekyll blogging on Windows 10</title>
      <dc:creator>Henrique</dc:creator>
      <pubDate>Thu, 05 Sep 2019 16:11:56 +0000</pubDate>
      <link>https://dev.to/hjgraca/using-wsl2-visual-studio-code-for-jekyll-blogging-on-windows-10-516g</link>
      <guid>https://dev.to/hjgraca/using-wsl2-visual-studio-code-for-jekyll-blogging-on-windows-10-516g</guid>
      <description>&lt;p&gt;If you are like me and have your blog hosted on GitHub pages you are using Jekyll as your blog generation engine. Turns out that Jekyll does not support Windows so you have to do some hacking in order to make it work on Windows. In this post I will show you how to take advantage of &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/wsl2-about"&gt;&lt;strong&gt;WSL2&lt;/strong&gt;&lt;/a&gt; and Ubuntu 18.04 to install Jekyll and locally run your blog.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Windows Subsystem for Linux 2?
&lt;/h3&gt;

&lt;p&gt;The Windows Subsystem for Linux (WSL) is a new Windows 10 feature that enables you to run native Linux command-line tools directly on Windows.&lt;/p&gt;

&lt;p&gt;WSL 2 is a new version of the architecture that powers the Windows Subsystem for Linux to run ELF64 Linux binaries on Windows. This new architecture changes how these Linux binaries interact with Windows and your computer’s hardware, but still provides the same user experience as in WSL 1 (the current widely available version). Individual Linux distros can be run either as a WSL 1 distro, or as a WSL 2 distro, can be upgraded or downgraded at any time, and you can run WSL 1 and WSL 2 distros side by side. WSL 2 uses an entirely new architecture that uses a real Linux kernel.&lt;/p&gt;

&lt;p&gt;How to install WSL2 is documented &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/wsl2-install"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Studio Remote WSL
&lt;/h3&gt;

&lt;p&gt;If you have VS Code already installed you will need to install the Remote WSL extension so that you are able to run linux commands and applications against your mounted Windows folder where your Jekyll blog is.&lt;/p&gt;

&lt;p&gt;Extension is &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the extension is installed you will see a small icon on the bottom left of VS Code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CfV8HGYj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/65/1%2A9WQv38Ec3lCDPtacVpF22A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CfV8HGYj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/65/1%2A9WQv38Ec3lCDPtacVpF22A.png" alt="" width="65" height="59"&gt;&lt;/a&gt;VS Code Remote icon&lt;/p&gt;

&lt;p&gt;What I usually do is open the folder that as the blog before starting the remote session. Once I have the folder open in VS Code I click on the Remote WSL icon and are presented with the following options:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AkqGpeU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ADfah7F6wfj7jOkYNovh9_A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AkqGpeU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ADfah7F6wfj7jOkYNovh9_A.png" alt="" width="880" height="521"&gt;&lt;/a&gt;Remote WSL options&lt;/p&gt;

&lt;p&gt;You will then choose &lt;strong&gt;Remote-WSL: Reopen Folder in WSL&lt;/strong&gt; (if you had the folder opened). Once everything is finished the icon will change to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iUdpRffy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/247/1%2A7woqt-9S1PTtl-NQRygCfA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iUdpRffy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/247/1%2A7woqt-9S1PTtl-NQRygCfA.png" alt="" width="247" height="62"&gt;&lt;/a&gt;Session established&lt;/p&gt;

&lt;p&gt;Now open the terminal window and you will see that the context is your Ubuntu machine and the folder is a mount to your Jekyll blog folder&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Installing Jekyll in WSL Ubuntu 18.04
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And now you can run your blog locally in Windows 10 with the power of WSL2 and Linux without having to hack your way installing Jekyll on Windows.&lt;/p&gt;

</description>
      <category>visualstudiocode</category>
      <category>windows10</category>
      <category>jekyll</category>
      <category>wsl2</category>
    </item>
  </channel>
</rss>
