<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>atverma</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/atverma/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/atverma/feed.xml</link>
    <description>Design and code need to be coherent.</description>
    <lastBuildDate>Tue, 16 Apr 2019 04:42:41 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Deploy SonarQube to Azure Kubernetes Service cluster and integrate with Azure DevOps build pipeline</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/deploy-sonarqube-to-azure-kubernetes-service-cluster-and-integrate-with-azure-devops-build-pipeline</link>
      <pubDate>Mon, 26 Nov 2018 06:29:29 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=1355</guid>
      <description><![CDATA[This is the next part of the series on developing and deploying

 	Angular, ASP.NET Core Web API...]]></description>
      <content:encoded><![CDATA[This is the next part of the series on developing and deploying
<ul>
 	<li>Angular, ASP.NET Core Web API and SQL Server to Azure Kubernetes Service</li>
 	<li>Function Apps using Azure Functions 2.0 runtime</li>
</ul>
In this article, I am going to share steps needed to deploy SonarQube to Azure Kubernetes Service cluster and integrate with Azure DevOps pipeline to setup code analysis for Angular and ASP.NET Core web apps created in previous parts of this series. The previous articles of this series are

https://blogs.msdn.microsoft.com/atverma/2018/09/16/azure-kubernetes-service-aks-deploying-angular-asp-net-core-and-sql-server-on-linux/

https://blogs.msdn.microsoft.com/atverma/2018/10/19/asp-net-core-2-1-web-api-load-app-configuration-from-appsettings-json-dockerfile-environment-variables-azure-key-vault-secrets-and-kubernetes-configmaps-secrets/

https://blogs.msdn.microsoft.com/atverma/2018/09/26/azure-functions-2-0-create-debug-and-deploy-to-azure-kubernetes-service-aks/

https://blogs.msdn.microsoft.com/atverma/2018/10/29/azure-functions-2-0-create-function-app-from-docker-image-functions-triggered-by-cosmos-db-blob-storage-event-hub-and-signalr-service-bindings

https://blogs.msdn.microsoft.com/atverma/2018/11/02/add-real-time-web-functionality-to-angular-application-using-asp-net-core-signalr-azure-signalr-service-and-azure-signalr-service-bindings-for-azure-functions-2-0/

<span>The tools used to develop these components are Visual Studio for Mac/VS Code/VS 2017, AKS Dashboard, Docker for Desktop and kubectl. </span>
<h2>SonarQube</h2>
<span><a target="_blank" href="https://Continuous Inspection SonarQube provides the capability to not only show health of an application but also to highlight issues newly introduced." rel="noopener">SonarQube</a> provides the capability to not only show health of an application but also to highlight issues newly introduced. I am going to configure SQL Server as backend database for SonarQube.</span>
<h3>Create a SQL Server Database</h3>
Create a SQL Server database named 'SonarDB' in the existing SQL server instance I had created during the first part of this series. SonarQube requires that database collation must be case-sensitive and accent-sensitive. Either you can specify collation while creating database or update it e.g code snippets to create/change collation to SQL_Latin1_General_CP1_CS_AS are

<code>CREATE DATABASE SonarDB
COLLATE SQL_Latin1_General_CP1_CS_AS;</code>

<code>ALTER DATABASE SonarDB
COLLATE SQL_Latin1_General_CP1_CS_AS;</code>

Create a SQL Login account which SonarQube needs to connect to 'SonarDB' database.
<h3>Create a Persistent Volume</h3>
Persistent volume claim is needed to store SonarQube data. The yaml snippet to create a 5 GB storage is<span class="Apple-converted-space"> displayed below. The deployment resource is going to mount files to this storage claim. You can read more about<span> </span><a target="_blank" href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" rel="noopener">Persistent Volumes.</a></span>

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> PersistentVolumeClaim</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-data-claim</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">accessModes</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">ReadWriteOnce</span>
<span style="color: #222222">  </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">   </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">storage</span><span style="color: #222222">:</span><span style="color: #db7100"> 5Gi</span></span></code>
<h3>Create a Kubernetes Service</h3>
<span>The next step is to create a Kubernetes Service for SonarQube. As you can see in yaml snippet below, port 80/9000 is defined and type is LoadBalancer i.e. this service will have external endpoints. Kubernetes will use to selector '</span><code>app: sonar-deployment</code><span>' to map to the deployment as you are going to see next. You can read more about</span><span> </span><a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener">Services</a><span>.</span><span class="Apple-converted-space"> </span>

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Service</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-svc</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-svc</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> LoadBalancer</span>
<span style="color: #222222">  </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">protocol</span><span style="color: #222222">:</span><span style="color: #db7100"> TCP</span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 80</span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9000</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">app</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-deployment</span></span></code>
<h3>Create a Kubernetes Deployment</h3>
The next step is to create a Kubernetes Deployment for SonarQube. The yaml snippet is displayed below and a few pointers are
<ul>
 	<li><span>The docker image being used to create this resource is <code>image: sonarqube</code></span></li>
 	<li>You can change the number of pods by updating <code>replicas: 1</code></li>
 	<li>Label <code>app: sonar-deployment</code> has to match the selector defined in the service</li>
 	<li><span>Persistent volume claim created above is used for storage i.e. </span><code>persistentVolumeClaim: claimName:sonar-data-claim</code><span>.</span></li>
 	<li>JDBC username, password and URL are specified which need to be updated based on values specific to your 'SonarDB' database and login user.</li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> extensions/v1beta1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Deployment</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-deployment</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">app</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-deployment</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">app</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-deployment</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">terminationGracePeriodSeconds</span><span style="color: #222222">:</span><span style="color: #db7100"> 15</span>
<span style="color: #222222">      </span><span style="color: #3363a4">initContainers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> fix-the-volume-permission</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> busybox</span>
<span style="color: #222222">        </span><span style="color: #3363a4">command</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">sh</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">-c</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">chown</span><span style="color: #db7100"> -R 1000:1000 /opt/sonarqube/extensions</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube-data</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /opt/sonarqube/extensions</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> fix-the-volume-permission1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> busybox</span>
<span style="color: #222222">        </span><span style="color: #3363a4">command</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">sh</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">-c</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">chown</span><span style="color: #db7100"> -R 1000:1000 /opt/sonarqube/data</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube-data</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /opt/sonarqube/data</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube</span>
<span style="color: #222222">          </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9000</span>
<span style="color: #222222">        </span><span style="color: #3363a4">env</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> SONARQUBE_JDBC_USERNAME</span>
<span style="color: #222222">          </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> SONARQUBE_JDBC_PASSWORD</span>
<span style="color: #222222">          </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> SONAR_SQL_LOGIN_PASSWORD</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> SONARQUBE_JDBC_URL </span>
<span style="color: #222222">          </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> jdbc:sqlserver://mssql-deployment:1433;databaseName=sonardb;user=sonar;password=SONAR_SQL_LOGIN_PASSWORD</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">         </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube-data</span>
<span style="color: #222222">           </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /opt/sonarqube/extensions</span>
<span style="color: #222222">           </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> extensions</span>
<span style="color: #222222">         </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube-data</span>
<span style="color: #222222">           </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /opt/sonarqube/data</span>
<span style="color: #222222">           </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> data</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sonarqube-data</span>
<span style="color: #222222">        </span><span style="color: #3363a4">persistentVolumeClaim</span><span style="color: #222222">:</span>
<span style="color: #222222">           </span><span style="color: #3363a4">claimName</span><span style="color: #222222">:</span><span style="color: #db7100"> sonar-data-claim</span></span></code>

After SonarQube resources are deployed to AKS cluster, navigate to the public endpoint and login using default credentials i.e. username and password is 'admin'. You can change the password. For this sample built in users are provisioned for authentication however you can configure external identity providers. You can read more about <a target="_blank" href="https://docs.sonarqube.org/latest/instance-administration/security" rel="noopener">external identity providers and http headers based authentication</a>.
<h3>Enable SonarQube plugin in Azure DevOps</h3>
You need to enable SonarQube extension in Azure DevOps. Browse for SonarQube extension in marketplace and enable it.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.43.04-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.43.04-PM-1024x345.png" alt="" width="703" height="237" class="alignnone wp-image-1425" /></a>
<h3>Generate Token in SonarQube</h3>
Azure DevOps will need token to connect to SonarQube instance. You need to generate token in SonarQube portal by navigating to <code>SonarQube &gt; Security &gt; Generate Tokens</code>. Keep a note of the token.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.18.16-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.18.16-PM-1024x543.png" alt="" width="499" height="265" class="alignnone wp-image-1385" /></a>
<h3>Create Projects in SonarQube</h3>
The next step is to create projects for Angular App and ASP.NET Core Web API in SonarQube portal. The project keys will be needed in Azure DevOps build pipeline thus keep a note of these values.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.05.16-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.05.16-PM-1024x253.png" alt="" width="685" height="169" class="alignnone wp-image-1365" /></a>

&nbsp;
<h3>Create a new Service Connection in Azure DevOps</h3>
In order to connect Azure DevOps to SonarQube, you will need to create a new service connection in Azure DevOps. The main pointers are
<ul>
 	<li>Specify a connection name</li>
 	<li>Specify the Server Url as endpoint address of SonarQube</li>
 	<li>Specify Token value which is generated in previous step</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.14.01-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.14.01-PM-1024x579.png" alt="" width="492" height="278" class="alignnone wp-image-1375" /></a>
<h3>Setting up Azure DevOps build pipeline for Angular App</h3>
The three steps needed for SonarQube integration barring <code>npm install</code> and <code>npm build</code> are
<ul>
 	<li>Prepare Analysis Configuration
<ul>
 	<li>This step needs to be run before build</li>
 	<li>Select SonarQube server endpoint</li>
 	<li>Specify Project Key and Project Name for Angular App</li>
 	<li>Specify Sources directory root i.e. path to root directory containing source files</li>
</ul>
</li>
 	<li>Run Code Analysis
<ul>
 	<li>This step needs to be run after build</li>
</ul>
</li>
 	<li>Publish Quality Gate Result</li>
</ul>
<h3><a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.29.47-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.29.47-PM-1024x552.png" alt="" width="761" height="410" class="alignnone wp-image-1405" /></a></h3>
<h3>Setting up Azure DevOps build pipeline for ASP.NET Core Web API App</h3>
The three steps needed for SonarQube integration barring <code>Restore</code> and <code>Build</code> are
<ul>
 	<li>Prepare Analysis Configuration
<ul>
 	<li>This step needs to be run before build</li>
 	<li>Select SonarQube server endpoint</li>
 	<li>Specify Project Key for ASP.NET Core Web API App</li>
 	<li>Select integrate with MSBuild option</li>
</ul>
</li>
 	<li>Run Code Analysis
<ul>
 	<li>This step needs to be run after build</li>
</ul>
</li>
 	<li>Publish Quality Gate Result</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.25.54-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.25.54-PM-1024x521.png" alt="" width="776" height="395" class="alignnone wp-image-1395" /></a>
<h3>Queue Angular and ASP.NET Core Web API Builds</h3>
Queue Angular and ASP.NET Core Web API builds on you can see detailed analysis in SonarQube portal.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.32.36-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-25-at-9.32.36-PM-1024x169.png" alt="" width="679" height="112" class="alignnone wp-image-1415" /></a>

This completes this article on deploying SonarQube to Azure Kubernetes Service cluster and integration with Azure DevOps build pipeline. The Kubernetes resources needed to deploy SonarQube to AKS cluster can be downloaded from <a href="https://github.com/atverma/AKS-Angular-AspnetCore-SQLServer/tree/master/sonarqube">GitHub</a>.

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Add real-time web functionality to Angular application using ASP.NET Core SignalR, Azure SignalR service and Azure SignalR Service bindings for Azure Functions 2.0</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/add-real-time-web-functionality-to-angular-application-using-asp-net-core-signalr-azure-signalr-service-and-azure-signalr-service-bindings-for-azure-functions-2-0</link>
      <pubDate>Fri, 02 Nov 2018 21:51:08 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=1255</guid>
      <description><![CDATA[This is the next part of the series on developing and deploying

 	Angular, ASP.NET Core Web API...]]></description>
      <content:encoded><![CDATA[This is the next part of the series on developing and deploying
<ul>
 	<li>Angular, ASP.NET Core Web API and SQL Server to Azure Kubernetes Service</li>
 	<li>Function Apps using Azure Functions 2.0 runtime</li>
</ul>
In this article I am going to go through steps needed to add real-time web functionality to Angular App using ASP.NET Core SignalR and Azure SignalR Service bindings for Azure Functions 2.0. The specific topics which this article is going to cover are
<ul>
 	<li>Add ASP.NET Core SignalR to ASP.NET Core 2.1 Web API
<ul>
 	<li>ASP.NET Core SignalR</li>
 	<li>ASP.NET Core SignalR scale out using
<ul>
 	<li>Azure SignalR Service backplane</li>
 	<li>Redis Cache backplane</li>
</ul>
</li>
</ul>
</li>
 	<li>Publish/Subscribe messages to SignalR Hub from Angular App</li>
 	<li>Publish/Subscribe messages to SignalR Hub using Azure SignalR Service bindings for Azure Functions 2.0 from Angular App</li>
 	<li>Build Docker images and deploy to Azure Kubernetes Service</li>
</ul>
The previous articles of this series are

https://blogs.msdn.microsoft.com/atverma/2018/09/16/azure-kubernetes-service-aks-deploying-angular-asp-net-core-and-sql-server-on-linux/

https://blogs.msdn.microsoft.com/atverma/2018/10/19/asp-net-core-2-1-web-api-load-app-configuration-from-appsettings-json-dockerfile-environment-variables-azure-key-vault-secrets-and-kubernetes-configmaps-secrets/

https://blogs.msdn.microsoft.com/atverma/2018/09/26/azure-functions-2-0-create-debug-and-deploy-to-azure-kubernetes-service-aks/

https://blogs.msdn.microsoft.com/atverma/2018/10/29/azure-functions-2-0-create-function-app-from-docker-image-functions-triggered-by-cosmos-db-blob-storage-event-hub-and-signalr-service-bindings

<span>The tools used to develop these components are Visual Studio for Mac/VS Code/VS 2017, AKS Dashboard, Docker for Desktop and kubectl. </span>
<h2>Add ASP.NET Core SignalR to ASP.NET Core 2.1 Web API</h2>
<span>ASP.NET Core SignalR is an open-source library that simplifies adding real-time web functionality to apps. Real-time web functionality enables server-side code to push content to clients instantly. The next sections are going to go through changes needed to add SignalR to ASP.NET Core 2.1 Web API along with SignalR scale out using Azure SignalR Service and Redis backplanes.</span>
<h3>ASP.NET Core SignalR</h3>
The main pointers about component diagram displayed below are
<ul>
 	<li>On navigating to Angular App (Browser), user will be authenticated and Access Token will be retrieved from Azure AD using MSAL</li>
 	<li>Angular App will pass the bearer token (JWT) in requests and ASP.NET Core 2.1 Web API will validate the Access Token</li>
 	<li>Angular App will establish connection to ASP.NET Core SignalR Hub (Web API) and publish/subscribe Messages</li>
 	<li>Angular App will establish connection to Azure SignalR Service using Function App endpoint and publish/subscribe Messages</li>
 	<li>Angular App, ASP.NET Core Web API and SQL Server are hosted in Azure Kubernetes Service. I have color coded the Pods assuming two Node configuration.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.00-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.00-PM-1024x534.png" alt="" width="789" height="411" class="alignnone wp-image-1295" /></a> <a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.32-PM.png"></a>

The steps needed to enable ASP.NET Core SignalR in ASP.NET Core 2.1 Web API are
<ul>
 	<li><strong>Configure SignalR Hub:</strong> <span>The SignalR Hubs API enables you to call methods on connected clients from the server. In the server code, you define methods that are called by client. In the client code, you define methods that are called from the server. SignalR takes care of everything behind the scenes that makes real-time client-to-server and server-to-client communications possible. Add <code><span style="color: #3363a4">INotificationHub</span></code> interface and<code><span style="color: #3363a4">NotificationHub</span></code>  class which are needed to define methods that are called by client.</span></li>
</ul>
<code><span> <span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">interface</span><span style="color: #222222"> </span><span style="color: #3363a4">INotificationHub</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #3363a4">Task</span><span style="color: #222222"> </span><span style="color: #222222">MessageNotification</span><span style="color: #222222">(</span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">message</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #3363a4">Task</span><span style="color: #222222"> </span><span style="color: #222222">PublishMessageAck</span><span style="color: #222222">(</span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">value</span><span style="color: #222222">);</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span></span></code>

<code><span> <span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">class</span><span style="color: #222222"> </span><span style="color: #3363a4">NotificationHub</span><span style="color: #222222"> </span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #3363a4">Hub</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">INotificationHub</span><span style="color: #222222">&gt;</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">async</span><span style="color: #222222"> </span><span style="color: #3363a4">Task</span><span style="color: #222222"> </span><span style="color: #222222">PublishMessage</span><span style="color: #222222">(</span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">message</span><span style="color: #222222">)</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #009695">this</span><span style="color: #222222">.</span><span style="color: #222222">Clients</span><span style="color: #222222">.</span><span style="color: #222222">AllExcept</span><span style="color: #222222">(</span><span style="color: #009695">this</span><span style="color: #222222">.</span><span style="color: #222222">Context</span><span style="color: #222222">.</span><span style="color: #222222">ConnectionId</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">MessageNotification</span><span style="color: #222222">(</span><span style="color: #db7100">$"Broadcast: </span><span style="color: #222222">{</span><span style="color: #222222">message</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">);</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #009695">this</span><span style="color: #222222">.</span><span style="color: #222222">Clients</span><span style="color: #222222">.</span><span style="color: #222222">Caller</span><span style="color: #222222">.</span><span style="color: #222222">PublishMessageAck</span><span style="color: #222222">(</span><span style="color: #db7100">$"Broadcast Ack: </span><span style="color: #222222">{</span><span style="color: #222222">message</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">override</span><span style="color: #222222"> </span><span style="color: #009695">async</span><span style="color: #222222"> </span><span style="color: #3363a4">Task</span><span style="color: #222222"> </span><span style="color: #222222">OnConnectedAsync</span><span style="color: #222222">()</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #222222">Groups</span><span style="color: #222222">.</span><span style="color: #222222">AddToGroupAsync</span><span style="color: #222222">(</span><span style="color: #222222">Context</span><span style="color: #222222">.</span><span style="color: #222222">ConnectionId</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"Users"</span><span style="color: #222222">);</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #009695">base</span><span style="color: #222222">.</span><span style="color: #222222">OnConnectedAsync</span><span style="color: #222222">();</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">override</span><span style="color: #222222"> </span><span style="color: #009695">async</span><span style="color: #222222"> </span><span style="color: #3363a4">Task</span><span style="color: #222222"> </span><span style="color: #222222">OnDisconnectedAsync</span><span style="color: #222222">(</span><span style="color: #3363a4">Exception</span><span style="color: #222222"> </span><span style="color: #222222">exception</span><span style="color: #222222">)</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #222222">Groups</span><span style="color: #222222">.</span><span style="color: #222222">RemoveFromGroupAsync</span><span style="color: #222222">(</span><span style="color: #222222">Context</span><span style="color: #222222">.</span><span style="color: #222222">ConnectionId</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"Users"</span><span style="color: #222222">);</span>
<span style="color: #222222">            </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #009695">base</span><span style="color: #222222">.</span><span style="color: #222222">OnDisconnectedAsync</span><span style="color: #222222">(</span><span style="color: #222222">exception</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span></span></code>
<ul>
 	<li>Specify <code><span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">SingalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222">.</span><span style="color: #222222">ASPNetCoreSignalR</span></span></code> in Startup.cs class</li>
 	<li>Update ConfigureServices method and add SignalR service.</li>
</ul>
<code><span> <span style="color: #222222">           </span><span style="color: #222222">services</span><span style="color: #222222">.</span><span style="color: #222222">AddSignalR</span><span style="color: #222222">(</span><span style="color: #222222">options</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">           </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">options</span><span style="color: #222222">.</span><span style="color: #222222">EnableDetailedErrors</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #009695">true</span><span style="color: #222222">;</span>
<span style="color: #222222">           </span><span style="color: #222222">})</span><span style="color: #222222">;</span></span></code>
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>Update Configure method and add SignalR to <code>IApplicationBuilder</code> request execution pipeline. The SignalRHub variable value I have specified in sample is  <code>/api/SignalRHub</code>.</li>
</ul>
</li>
</ul>
<code><span>          </span><span style="color: #222222">app</span><span style="color: #222222">.</span><span style="color: #222222">UseSignalR</span><span style="color: #222222">((</span><span style="color: #222222">options</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">          </span><span style="color: #222222">{</span>
<span style="color: #222222">              </span><span style="color: #222222">options</span><span style="color: #222222">.</span><span style="color: #222222">MapHub</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">NotificationHub</span><span style="color: #222222">&gt;(</span><span style="color: #222222">SignalRHub</span><span style="color: #222222">);</span>
<span style="color: #222222">          </span><span style="color: #222222">});</span></code>
<ul>
 	<li>Allow the JWT authentication handler to read the access <span class="hljs-comment">token from the query string when a WebSocket or server sent events request comes in <code>Service.AddAuthentication(...).AddJwtBearer(...)</code> method.</span></li>
</ul>
<code><span><span style="color: #222222">          OnMessageReceived</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #222222">context</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">         {</span>
<span style="color: #222222">              </span><span style="color: #222222">var</span><span style="color: #222222"> </span><span style="color: #222222">accessToken</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #222222">context</span><span style="color: #222222">.</span><span style="color: #222222">Request</span><span style="color: #222222">.</span><span style="color: #222222">Query</span><span style="color: #222222">[</span><span style="color: #db7100">"access_token"</span><span style="color: #222222">];</span>
<span style="color: #222222">              </span><span style="color: #222222">var</span><span style="color: #222222"> </span><span style="color: #222222">path</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #222222">context</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Request</span><span style="color: #222222">.</span><span style="color: #222222">Path</span><span style="color: #222222">;</span>
<span style="color: #222222">              </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">!</span><span style="color: #009695">string</span><span style="color: #222222">.</span><span style="color: #222222">IsNullOrEmpty</span><span style="color: #222222">(</span><span style="color: #222222">accessToken</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">&amp;&amp;</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">path</span><span style="color: #222222">.</span><span style="color: #222222">StartsWithSegments</span><span style="color: #222222">(</span><span style="color: #222222">SignalRHub</span><span style="color: #222222">)))</span>
<span style="color: #222222">              </span><span style="color: #222222">{</span>
<span style="color: #222222">                  </span><span style="color: #222222">context</span><span style="color: #222222">.</span><span style="color: #222222">Token</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #222222">accessToken</span><span style="color: #222222">;</span>
<span style="color: #222222">              </span><span style="color: #222222">}</span>
<span style="color: #222222">              </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #222222">Task</span><span style="color: #222222">.</span><span style="color: #222222">CompletedTask</span><span style="color: #222222">;</span>
<span style="color: #222222">          },</span></span></code>
<ul>
 	<li>Claims of the authorized user can be accessed by calling <code><span><span style="color: #222222">Context.User.Claims</span></span></code> in NotificationHub class.</li>
</ul>
In a single node/server scenario this works fine however when ASP.NET Core 2.1 Web API is scaled out e.g. when running more than one Pods, clients connected to SignalR Hub on one Pod won't receive message published on SignalR Hub on another Pod. ASP.NET Core SignalR supports Redis Cache and Azure SignalR Service scale out.
<h3>ASP.NET Core SignalR scale out using Redis Cache backplane</h3>
Redis cache can be used as backplane for ASP.NET Core SignalR scale out. Redis cache is used as Pub/Sub to forward messages to other servers. This option needs sticky sessions to be enabled. The only change in component diagram from previous implementation is that Web API Pods are communicating with Redis Cache service.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.32-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.32-PM-1024x577.png" alt="" width="890" height="501" class="alignnone wp-image-1315" /></a>

The code changes needed to use Redis backplane are
<ul>
 	<li>Install <code>Microsoft.AspNetCore.SignalR.Redis</code> Nuget package</li>
 	<li>Specify <code><span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">SingalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222">.</span><span style="color: #222222">Redis</span></span></code> in Startup.cs class</li>
 	<li>Update ConfigureServices method and add <code><span><span style="color: #222222">AddRedis</span></span></code></li>
 	<li style="list-style-type: none"></li>
</ul>
<code><span> <span style="color: #222222">            </span><span style="color: #222222">services</span><span style="color: #222222">.</span><span style="color: #222222">AddSignalR</span><span style="color: #222222">(</span><span style="color: #222222">options</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">            </span><span style="color: #222222">{</span>
<span style="color: #222222">               </span><span style="color: #222222">options</span><span style="color: #222222">.</span><span style="color: #222222">EnableDetailedErrors</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #009695">true</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">})</span><span style="color: #222222">.</span><span style="color: #222222">AddRedis</span><span style="color: #222222">(</span><span style="color: #222222">RedisConnectionString</span><span style="color: #222222">);</span></span></code>
<ul>
 	<li>Specify value of <code>"RedisConnectionString": "REDIS_CONNECTION_STRING"</code> in appsettings.json</li>
</ul>
<h3>ASP.NET Core SignalR scale out using Azure SignalR Service backplane</h3>
Azure SignalR Service can be used as backplane for ASP.NET Core SignalR scale out. This option doesn't need sticky sessions to be enabled.<span>One of the key reasons to use the Azure SignalR Service is simplicity. With Azure SignalR Service, you don't need to handle problems like performance, scalability, availability. </span>The only change in component diagram from previous implementation is Web API Pods are communicating with Azure SignalR Service.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.13-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.14.13-PM-1024x539.png" alt="" width="822" height="432" class="alignnone wp-image-1305" /></a>

The code changes needed to use Azure SignalR service are
<ul>
 	<li>Install <code>Microsoft.Azure.SignalR</code> Nuget package</li>
 	<li>Specify <code><span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">SingalRConfigMode</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #3363a4">SignalRConfigMode</span><span style="color: #222222">.</span><span style="color: #222222">AzureSignalRService</span></span></code> in Startup.cs class</li>
 	<li>Update ConfigureServices method and add <code><span><span style="color: #222222">AddAzureSignalR</span></span></code></li>
</ul>
<code><span> <span style="color: #222222">           </span><span style="color: #222222">services</span><span style="color: #222222">.</span><span style="color: #222222">AddSignalR</span><span style="color: #222222">(</span><span style="color: #222222">options</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">           </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">options</span><span style="color: #222222">.</span><span style="color: #222222">EnableDetailedErrors</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #009695">true</span><span style="color: #222222">;</span>
<span style="color: #222222">           </span><span style="color: #222222">})</span><span style="color: #222222">.</span><span style="color: #222222">AddAzureSignalR</span><span style="color: #222222">(</span><span style="color: #222222">configure</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">           </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">configure</span><span style="color: #222222">.</span><span style="color: #222222">ConnectionString</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #222222">AzureSignalRConnectionString</span><span style="color: #222222">;</span>
<span style="color: #222222">           </span><span style="color: #222222">});</span></span></code>
<ul>
 	<li>Update Configure method and add <code><span style="color: #222222">UseAzureSignalR</span></code></li>
</ul>
<code><span>          </span><span style="color: #222222">app</span><span style="color: #222222">.</span><span style="color: #222222">UseAzureSignalR</span><span style="color: #222222">((</span><span style="color: #222222">options</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">=&gt;</span>
<span style="color: #222222">          </span><span style="color: #222222">{</span>
<span style="color: #222222">              </span><span style="color: #222222">options</span><span style="color: #222222">.</span><span style="color: #222222">MapHub</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">NotificationHub</span><span style="color: #222222">&gt;(</span><span style="color: #222222">SignalRHub</span><span style="color: #222222">);</span>
<span style="color: #222222">          </span><span style="color: #222222">})</span></code>
<ul>
 	<li>Specify value of <code>"AzureSignalRConnectionString": "AZURE_SIGNALR_CONNECTION_STRING"</code> in appsettings.json</li>
</ul>
<h2>Publish/Subscribe messages to SignalR Hub from Angular App</h2>
Angular App sample is updated to publish/subscribe to SignalR Hub. The main pointers about code snippet listed below are
<ul>
 	<li>Install <code><span><span style="color: #db7100">aspnet/signalr</span></span></code> which provides support for JavaScript and TypeScript clients for <span>SignalR for ASP.NET Core</span></li>
 	<li>SignalR Hub URL is specified in <code><span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">API_URL</span><span style="color: #222222">}</span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">SIGNALR_HUB</span><span style="color: #222222">}</span></span></code></li>
 	<li>Access Token is needed to authorize requests to SignalR Hub and is specified in <code><span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #222222">accessTokenFactory</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">authHelper</span><span style="color: #222222">.</span><span style="color: #222222">getAccessTokenFromCache</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">}</span></span></code></li>
 	<li>Retry connection interval is <code><span><span style="color: #db7100">10000</span></span></code></li>
 	<li>App subscribes to <code><span><span style="color: #db7100">MessageNotification</span></span></code> and <code><span><span style="color: #db7100">PublishMessageAck</span></span></code> messages of SignalR Hub</li>
 	<li>App sends messages to SignalR Hub using <code><span><span style="color: #db7100">PublishMessage</span></span></code></li>
 	<li>Specify <code>'API_URL': 'WEB_API_URL'</code> in 'environment.ts'</li>
</ul>
<code><span> <span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">EventEmitter</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">Injectable</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">OnDestroy</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">OnInit</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@angular/core</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">HubConnection</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">HubConnectionBuilder</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@aspnet/signalr</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #009695">*</span><span style="color: #222222"> </span><span style="color: #009695">as</span><span style="color: #222222"> </span><span style="color: #009695">signalR</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@aspnet/signalr</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">AuthHelperService</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">AccessTokenInfo</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">./auth-helper.service</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">Config</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">./config/config</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #222222">@</span><span style="color: #222222">Injectable</span><span style="color: #222222">(</span><span style="color: #222222">{</span>
<span style="color: #222222">  </span><span style="color: #222222">providedIn</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">root</span><span style="color: #db7100">'</span><span style="color: #222222">,</span>
<span style="color: #222222">}</span><span style="color: #222222">)</span>
<span style="color: #009695">export</span><span style="color: #222222"> </span><span style="color: #009695">class</span><span style="color: #222222"> </span><span style="color: #222222">SignalRService</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">  </span><span style="color: #222222">messageReceived</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #222222">EventEmitter</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">string</span><span style="color: #222222">&gt;</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">hubConnection</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">HubConnection</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #009695">constructor</span><span style="color: #222222">(</span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">authHelper</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">AuthHelperService</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">config</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">Config</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #222222">init</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">createConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">createConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #222222">HubConnectionBuilder</span><span style="color: #222222">(</span><span style="color: #222222">)</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">withUrl</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">API_URL</span><span style="color: #222222">}</span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">SIGNALR_HUB</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #222222">accessTokenFactory</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">authHelper</span><span style="color: #222222">.</span><span style="color: #222222">getAccessTokenFromCache</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222">)</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">configureLogging</span><span style="color: #222222">(</span><span style="color: #009695">signalR</span><span style="color: #222222">.</span><span style="color: #009695">LogLevel</span><span style="color: #222222">.</span><span style="color: #009695">Information</span><span style="color: #222222">)</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">build</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">onclose</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">      </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">SignalR hub connection closed.</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">      </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">stopHubAndunSubscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">      </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #009695">err</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">Error</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #db7100">Error </span><span style="color: #222222">${</span><span style="color: #009695">err</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Retrying connection to SignalR Hub ...</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">setTimeout</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">      </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">10000</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">start</span><span style="color: #222222">(</span><span style="color: #222222">)</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">then</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">SignalR Hub connection started</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">subscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">      </span><span style="color: #222222">}</span><span style="color: #222222">)</span>
<span style="color: #222222">      </span><span style="color: #222222">.</span><span style="color: #222222">catch</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #009695">err</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">      </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #222222">publishMessage</span><span style="color: #222222">(</span><span style="color: #222222">message</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">string</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">invoke</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">PublishMessage</span><span style="color: #db7100">'</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">message</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">subscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">on</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">MessageNotification</span><span style="color: #db7100">'</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">data</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">any</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">      </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">messageReceived</span><span style="color: #222222">.</span><span style="color: #222222">emit</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">MessageNotification:</span><span style="color: #db7100">'</span><span style="color: #222222"> </span><span style="color: #009695">+</span><span style="color: #222222"> </span><span style="color: #009695">data</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">on</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">PublishMessageAck</span><span style="color: #db7100">'</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">data</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">any</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">      </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">messageReceived</span><span style="color: #222222">.</span><span style="color: #222222">emit</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">MessageNotification - Ack :</span><span style="color: #db7100">'</span><span style="color: #222222"> </span><span style="color: #009695">+</span><span style="color: #222222"> </span><span style="color: #009695">data</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">  </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">stopHubAndunSubscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">off</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">MessageNotification</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">off</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">PublishMessageAck</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">stop</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">then</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Hub connection stopped</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span>
</span></code>
<h2>Publish/Subscribe messages to SignalR Hub using Azure SignalR Service bindings for Azure Functions 2.0 from Angular App</h2>
The previous blog of this series describes steps needed to develop a Function App with<span> Azure SignalR Service bindings for Azure Functions 2.0. </span>Angular App sample is updated to publish/subscribe to Azure SignalR Service Hub through Azure Functions (you don't need to host a web application at all). The main pointers about code snippet listed below are
<ul>
 	<li>Install <code><span><span style="color: #db7100">aspnet/signalr</span></span></code> which provides support for JavaScript and TypeScript clients for <span>SignalR for ASP.NET Core</span></li>
 	<li><code><span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">FUNCTION_APP_URL</span><span style="color: #222222">}</span><span style="color: #db7100">negotiate</span></span></code> post request will return <code><span><span style="color: #222222">SignalRConnectionInfo</span></span></code> object having url and accessToken.</li>
 	<li><code><span><span style="color: #222222">SignalRConnectionInfo</span></span></code> is needed to connect to Azure SignalR Service Hub by specifying <code><span><span style="color: #222222">accessTokenFactory</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #009695">info</span><span style="color: #222222">.</span><span style="color: #009695">accessToken</span></span></code> and url in <code><span><span style="color: #222222">withUrl</span><span style="color: #222222">(</span><span style="color: #009695">url</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">options</span><span style="color: #222222">)</span></span></code><code><span><span style="color: #222222"></span></span></code></li>
 	<li>Retry connection interval is <code><span><span style="color: #db7100">10000</span></span></code></li>
 	<li>App sends messages to SignalR Hub by sending a post request to <code><span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">FUNCTION_APP_URL</span><span style="color: #222222">}</span><span style="color: #db7100">sendmessage</span></span></code></li>
 	<li>App subscribes to <code><span><span style="color: #db7100">SendMessage</span></span></code> i.e. <code><span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">on</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">sendMessage</span><span style="color: #db7100">'</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">data</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">any</span><span style="color: #222222">)</span></span></code> of SignalR Hub</li>
 	<li>Specify <code>'FUNCTION_APP_URL': 'FUNCTION_APP_URLFUNCTION_APP_URL'</code> in environment.ts</li>
</ul>
<code><span> <span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">Injectable</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">EventEmitter</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">OnDestroy</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@angular/core</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">HttpClient</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">HttpErrorResponse</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@angular/common/http</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">HubConnection</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">IHttpConnectionOptions</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@aspnet/signalr</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #009695">*</span><span style="color: #222222"> </span><span style="color: #009695">as</span><span style="color: #222222"> </span><span style="color: #009695">signalR</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">@aspnet/signalr</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">Observable</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">throwError</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">rxjs</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">SignalRConnectionInfo</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">./signalRConnectionInfo</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #009695">import</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #009695">Config</span><span style="color: #222222"> </span><span style="color: #222222">}</span><span style="color: #222222"> </span><span style="color: #009695">from</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">./config/config</span><span style="color: #db7100">'</span><span style="color: #222222">;</span>
<span style="color: #222222">@</span><span style="color: #222222">Injectable</span><span style="color: #222222">(</span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">providedIn</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">'</span><span style="color: #db7100">root</span><span style="color: #db7100">'</span>
<span style="color: #222222">}</span><span style="color: #222222">)</span>
<span style="color: #009695">export</span><span style="color: #222222"> </span><span style="color: #009695">class</span><span style="color: #222222"> </span><span style="color: #222222">SignalRFuncService</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">hubConnection</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">HubConnection</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">messageReceived</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #222222">EventEmitter</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">string</span><span style="color: #222222">&gt;</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #009695">constructor</span><span style="color: #222222">(</span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">http</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">HttpClient</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">config</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">Config</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">getConnectionInfo</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">Observable</span><span style="color: #222222">&lt;</span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">const</span><span style="color: #222222"> </span><span style="color: #222222">requestUrl</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #db7100">`</span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">FUNCTION_APP_URL</span><span style="color: #222222">}</span><span style="color: #db7100">negotiate</span><span style="color: #db7100">`</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">http</span><span style="color: #222222">.</span><span style="color: #222222">post</span><span style="color: #222222">&lt;</span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222">&gt;</span><span style="color: #222222">(</span><span style="color: #009695">requestUrl</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">null</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #222222">init</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">getConnectionInfo</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">subscribe</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">info</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #009695">const</span><span style="color: #222222"> </span><span style="color: #222222">options</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">accessTokenFactory</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #009695">info</span><span style="color: #222222">.</span><span style="color: #009695">accessToken</span>
<span style="color: #222222">            </span><span style="color: #222222">}</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">createConnection</span><span style="color: #222222">(</span><span style="color: #009695">info</span><span style="color: #222222">.</span><span style="color: #009695">url</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">options</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #222222">error</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">error</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #db7100">An error occurred during init: </span><span style="color: #222222">${</span><span style="color: #009695">error</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Retrying connection to Azure Function - SignalR Hub ...</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">setTimeout</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">init</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">}</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">10000</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span><span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">createConnection</span><span style="color: #222222">(</span><span style="color: #222222">url</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">string</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">options</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">IHttpConnectionOptions</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #222222">signalR</span><span style="color: #222222">.</span><span style="color: #222222">HubConnectionBuilder</span><span style="color: #222222">(</span><span style="color: #222222">)</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">withUrl</span><span style="color: #222222">(</span><span style="color: #009695">url</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">options</span><span style="color: #222222">)</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">configureLogging</span><span style="color: #222222">(</span><span style="color: #009695">signalR</span><span style="color: #222222">.</span><span style="color: #009695">LogLevel</span><span style="color: #222222">.</span><span style="color: #009695">Information</span><span style="color: #222222">)</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">build</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">onclose</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Azure Function - SignalR Hub connection closed.</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">stopHubAndunSubscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #009695">err</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">start</span><span style="color: #222222">(</span><span style="color: #222222">)</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">then</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Azure Function - SignalR Hub connection started.</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">                </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">subscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">}</span><span style="color: #222222">)</span>
<span style="color: #222222">            </span><span style="color: #222222">.</span><span style="color: #222222">catch</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #009695">err</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">            </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">restartConnection</span><span style="color: #222222">(</span><span style="color: #222222">err</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #222222">Error</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #db7100">Error </span><span style="color: #222222">${</span><span style="color: #009695">err</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Retrying connection to Azure Function - SignalR Hub ...</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">setTimeout</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #222222">startConnection</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">10000</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #222222">send</span><span style="color: #222222">(</span><span style="color: #222222">message</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">string</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">const</span><span style="color: #222222"> </span><span style="color: #222222">requestUrl</span><span style="color: #222222"> </span><span style="color: #009695">=</span><span style="color: #222222"> </span><span style="color: #db7100">`</span><span style="color: #222222">${</span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">config</span><span style="color: #222222">.</span><span style="color: #222222">get</span><span style="color: #db7100">(</span><span style="color: #db7100">)</span><span style="color: #222222">.</span><span style="color: #009695">FUNCTION_APP_URL</span><span style="color: #222222">}</span><span style="color: #db7100">sendmessage</span><span style="color: #db7100">`</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">http</span><span style="color: #222222">.</span><span style="color: #222222">post</span><span style="color: #222222">(</span><span style="color: #009695">requestUrl</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #009695">message</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">subscribe</span><span style="color: #222222">(</span>
<span style="color: #222222">            </span><span style="color: #222222">(</span><span style="color: #222222">data</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">any</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #db7100">Func Hub sendMessage: </span><span style="color: #222222">${</span><span style="color: #009695">message</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">)</span><span style="color: #222222">,</span>
<span style="color: #222222">            </span><span style="color: #222222">error</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">error</span><span style="color: #222222">(</span><span style="color: #db7100">`</span><span style="color: #db7100">An error occurred in sendMessage: </span><span style="color: #222222">${</span><span style="color: #009695">error</span><span style="color: #222222">}</span><span style="color: #db7100">`</span><span style="color: #222222">)</span>
<span style="color: #222222">        </span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">subscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">on</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">sendMessage</span><span style="color: #db7100">'</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">data</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">any</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">messageReceived</span><span style="color: #222222">.</span><span style="color: #222222">emit</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">MessageNotification - Function: </span><span style="color: #db7100">'</span><span style="color: #222222"> </span><span style="color: #009695">+</span><span style="color: #222222"> </span><span style="color: #009695">data</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">private</span><span style="color: #222222"> </span><span style="color: #222222">stopHubAndunSubscribeToServerEvents</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">void</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">off</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">sendMessage</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #222222">this</span><span style="color: #222222">.</span><span style="color: #009695">hubConnection</span><span style="color: #222222">.</span><span style="color: #222222">stop</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">then</span><span style="color: #222222">(</span><span style="color: #222222">(</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #009695">=&gt;</span><span style="color: #222222"> </span><span style="color: #3363a4">console</span><span style="color: #222222">.</span><span style="color: #222222">log</span><span style="color: #222222">(</span><span style="color: #db7100">'</span><span style="color: #db7100">Hub connection stopped</span><span style="color: #db7100">'</span><span style="color: #222222">)</span><span style="color: #222222">)</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span></span></code>

<code><span> <span style="color: #009695">export</span><span style="color: #222222"> </span><span style="color: #009695">class</span><span style="color: #222222"> </span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #222222">url</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">string</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">accessToken</span><span style="color: #009695">:</span><span style="color: #222222"> </span><span style="color: #3363a4">string</span><span style="color: #222222">;</span>
<span style="color: #222222">}</span></span></code>
<h2>Build Docker Images and Deploy to Azure Kubernetes Service</h2>
Build docker images for ASP.NET Core Web API and Angular App and deploy these docker images to Azure Kubernetes Service cluster. After deployment, browse to Angular App and send message either using ASP.NET Core SignalR or Azure Functions 2.0 endpoint.

<a href="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.51.33-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/11/Screen-Shot-2018-11-02-at-12.51.33-PM.png" alt="" width="561" height="481" class="alignnone wp-image-1335" /></a>
<h2>Summary</h2>
This completes the article on add real-time web functionality to Angular App using ASP.NET Core SignalR and Azure SignalR Service bindings for Azure Functions 2.0. The complete source code can be downloaded from <span><a target="_blank" href="https://github.com/atverma/azurekubernetesservice-azurefunctions-2.0" rel="noopener">GitHub-AzureFunctions</a> and <a target="_blank" href="https://github.com/atverma/AKS-Angular-AspnetCore-SQLServer" rel="noopener">GitHub-AKS</a>.</span><a target="_blank" href="https://github.com/atverma/AKS-Angular-AspnetCore-SQLServer" rel="noopener"></a>

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Azure Functions 2.0: Create Function App from Docker Image (Functions triggered by Cosmos DB, Blob storage, Event Hub and SignalR service bindings)</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-functions-2-0-create-function-app-from-docker-image-functions-triggered-by-cosmos-db-blob-storage-event-hub-and-signalr-service-bindings</link>
      <pubDate>Mon, 29 Oct 2018 23:16:22 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=1185</guid>
      <description><![CDATA[This is second part of the series on developing and deploying Azure Functions 2.0 where I will...]]></description>
      <content:encoded><![CDATA[This is second part of the series on developing and deploying Azure Functions 2.0 where I will
<ul>
 	<li>Create a function triggered by Azure Cosmos DB</li>
 	<li>Create Azure SignalR Service bindings for Azure Functions 2.0</li>
 	<li>Publish Docker Image to Docker Hub</li>
 	<li>Create Function App from Docker Image in Azure Portal</li>
 	<li>Deploy functions to Azure Kubernetes Service from VS Code</li>
</ul>
The first part of the series provides details on creating functions triggered by Azure Blob storage and Event hub in Visual Studio Code along with deploying Azure Functions to Azure Kubernetes Service (AKS).

https://blogs.msdn.microsoft.com/atverma/2018/09/26/azure-functions-2-0-create-debug-and-deploy-to-azure-kubernetes-service-aks/

<span>Dev tools used to develop these components are Visual Studio Code for macOS and Docker. The complete source code for this article can be downloaded from <a target="_blank" href="https://github.com/atverma/azurekubernetesservice-azurefunctions-2.0" rel="noopener">GitHub</a>.</span>
<h2>Create a function triggered by Azure Cosmos DB</h2>
The next steps will explain how to create a function triggered by data insert/change in Cosmos DB.
<h3>Create Azure Cosmos DB</h3>
I have specified following values for function
<ul>
 	<li>Name of database with the collection to be monitored as 'SampleDB'</li>
 	<li>Name of collection to be monitored as 'SampleCollection'</li>
 	<li>Name of collection used to store leases as 'leases'</li>
</ul>
Keep a note of the connection string as this needs to be specified in function.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-4.07.53-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-4.07.53-PM.png" alt="" width="505" height="387" class="alignnone wp-image-1225" /></a>
<h3>Create Azure Cosmos DB trigger</h3>
<span>Run command </span><code>func new</code><span> to create a new function and select <code>CosmosDBTrigger</code> option and specify function name. I have specified <code><span style="color: #222222">databaseName</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"SampleDB"</span></code>, <code><span style="color: #222222">collectionName</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"SampleCollection"</span></code> and  <code><span style="color: #222222">LeaseCollectionName</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"leases"</span></code> based on values I had specified in previous step.</span>

<code><span><span style="color: #222222">[</span><span style="color: #222222">FunctionName</span><span style="color: #222222">(</span><span style="color: #db7100">"TestCosmosDBTrigger"</span><span style="color: #222222">)]</span>
<span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">static</span><span style="color: #222222"> </span><span style="color: #009695">void</span><span style="color: #222222"> </span><span style="color: #222222">Run</span><span style="color: #222222">([</span><span style="color: #222222">CosmosDBTrigger</span><span style="color: #222222">(</span>
<span style="color: #222222">    </span><span style="color: #222222">databaseName</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"SampleDB"</span><span style="color: #222222">,</span>
<span style="color: #222222">    </span><span style="color: #222222">collectionName</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"SampleCollection"</span><span style="color: #222222">,</span>
<span style="color: #222222">    </span><span style="color: #222222">ConnectionStringSetting</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"TestCosmonDBConnection"</span><span style="color: #222222">,</span>
<span style="color: #222222">    </span><span style="color: #222222">LeaseCollectionName</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"leases"</span><span style="color: #222222">)]</span><span style="color: #3363a4">IReadOnlyList</span><span style="color: #222222">&lt;</span><span style="color: #222222">Document</span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #222222">input</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #3363a4">ILogger</span><span style="color: #222222"> </span><span style="color: #222222">log</span><span style="color: #222222">)</span>
<span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">input</span><span style="color: #222222"> </span><span style="color: #222222">!=</span><span style="color: #222222"> </span><span style="color: #009695">null</span><span style="color: #222222"> </span><span style="color: #222222">&amp;&amp;</span><span style="color: #222222"> </span><span style="color: #222222">input</span><span style="color: #222222">.</span><span style="color: #222222">Count</span><span style="color: #222222"> </span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #db7100">0</span><span style="color: #222222">)</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #222222">log</span><span style="color: #222222">.</span><span style="color: #222222">LogInformation</span><span style="color: #222222">(</span><span style="color: #db7100">"Documents modified "</span><span style="color: #222222"> </span><span style="color: #222222">+</span><span style="color: #222222"> </span><span style="color: #222222">input</span><span style="color: #222222">.</span><span style="color: #222222">Count</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">log</span><span style="color: #222222">.</span><span style="color: #222222">LogInformation</span><span style="color: #222222">(</span><span style="color: #db7100">"First document Id "</span><span style="color: #222222"> </span><span style="color: #222222">+</span><span style="color: #222222"> </span><span style="color: #222222">input</span><span style="color: #222222">[</span><span style="color: #db7100">0</span><span style="color: #222222">]</span><span style="color: #222222">.</span><span style="color: #222222">Id</span><span style="color: #222222">);</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span>
</span></code>

Update 'local.settings.json' and specify value for <code>"TestCosmonDBConnection":"{COSMOS_DB_CONNECTION_STRING}"</code> as this is needed to run host locally. You can start host by running command <code>func start --build<span class="Apple-converted-space">  </span>--debug VSCode<span class="Apple-converted-space">  </span>--cors "*"</code>

On mojave macOS, VS Code was not loading this function and the error message was '<span><em>Error reading Darwin Kernel Version. System.Private.CoreLib: The given key 'osx.10.14-x64' was not present in the dictionary</em>'. Adding </span><code>"DOTNET_RUNTIME_ID" : "osx-x64"</code> in 'local.settings.json' fixed that error for me.
<h2>Create Azure SignalR Service bindings for Azure Functions</h2>
The next steps will explain how to <span>authenticate and send real-time messages to clients connected to </span><a href="https://azure.microsoft.com/services/signalr-service/">Azure SignalR Service</a><span> by using SignalR Service bindings in Azure Functions. The SignalR Service bindings are provided in the <a href="https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.SignalRService">Microsoft.Azure.WebJobs.Extensions.SignalRService</a> NuGet package. The command to install package in VS Code is <code>func extensions install -p Microsoft.Azure.WebJobs.Extensions.SignalRService -v 1.0.0-preview1-10002</code></span>
<h3>Create Azure SignalR Service</h3>
Specify the resource name and resource group and keep a note of the connection string since this is needed in <span>HTTP triggers which will invoke a function with a HTTP request</span>.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-4.32.05-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-4.32.05-PM.png" alt="" width="331" height="534" class="alignnone wp-image-1235" /></a>
<h3>Create HttpTrigger to retrieve the service endpoint URL and valid access token of Azure SignalR Service</h3>
Before a client can connect to Azure SignalR Service, it must retrieve the service endpoint URL and a valid access token. The <code>SignalRConnectionInfo</code> input binding produces the SignalR Service endpoint URL and a valid token that are used to connect to the service. I have specified <code><span style="color: #222222">HubName</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"testazuresignalrhub"</span></code> as hub name. The client app is going to send http post request to 'https://{FUNCTION_URL}/api/negotiate'.

<code><span> <span style="color: #222222">[</span><span style="color: #222222">FunctionName</span><span style="color: #222222">(</span><span style="color: #db7100">"negotiate"</span><span style="color: #222222">)]</span>
<span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">static</span><span style="color: #222222"> </span><span style="color: #3363a4">IActionResult</span><span style="color: #222222"> </span><span style="color: #222222">GetSignalRInfo</span><span style="color: #222222">(</span>
<span style="color: #222222">    </span><span style="color: #222222">[</span><span style="color: #222222">HttpTrigger</span><span style="color: #222222">(</span><span style="color: #222222">AuthorizationLevel</span><span style="color: #222222">.</span><span style="color: #222222">Anonymous</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"post"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"options"</span><span style="color: #222222">)]</span><span style="color: #3363a4">HttpRequest</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">,</span>
<span style="color: #222222">    </span><span style="color: #222222">[</span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222">(</span><span style="color: #222222">HubName</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"testazuresignalrhub"</span><span style="color: #222222">)]</span><span style="color: #222222">SignalRConnectionInfo</span><span style="color: #222222"> </span><span style="color: #222222">connectionInfo</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #3363a4">ILogger</span><span style="color: #222222"> </span><span style="color: #222222">log</span><span style="color: #222222">)</span>
<span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #009695">try</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">!</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Credentials"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Credentials"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"true"</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Origin"</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">&amp;&amp;</span><span style="color: #222222"> </span><span style="color: #222222">!</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Origin"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Origin"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">[</span><span style="color: #db7100">"Origin"</span><span style="color: #222222">][</span><span style="color: #db7100">0</span><span style="color: #222222">]);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Request-Headers"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Headers"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">[</span><span style="color: #db7100">"access-control-request-headers"</span><span style="color: #222222">][</span><span style="color: #db7100">0</span><span style="color: #222222">]);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #222222">log</span><span style="color: #222222">.</span><span style="color: #222222">LogInformation</span><span style="color: #222222">(</span><span style="color: #db7100"><span style="color: #db7100">"negotiate API succeeded."</span></span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">connectionInfo</span><span style="color: #222222"> </span><span style="color: #222222">==</span><span style="color: #222222"> </span><span style="color: #009695">null</span><span style="color: #222222">)</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">NotFoundObjectResult</span><span style="color: #222222">(</span><span style="color: #db7100">"Azure SignalR not found."</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">OkObjectResult</span><span style="color: #222222">(</span><span style="color: #222222">connectionInfo</span><span style="color: #222222">);</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">catch</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">BadRequestResult</span><span style="color: #222222">();</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span></span><code></code></code>
<h3>Create HttpTrigger to send message using Azure SignalR Service</h3>
The client will use URL and access token from <code>SignalRConnectionInfo</code> retrieved in previous step to establish SignalR connection. It will then u<span>se </span>SignalR<span> output binding to send one or more messages using Azure SignalR Service. The client app is going to send http 'post' request to 'https://{FUNCTION_URL}/api/sendmessage' and provide string payload. This API sends message to all clients who have subscribed to <code><span style="font-family: Menlo"><span style="color: #db7100">sendMessage</span></span></code>.</span>

<code><span style="font-family: Menlo"> <span style="color: #222222">[</span><span style="color: #222222">FunctionName</span><span style="color: #222222">(</span><span style="color: #db7100">"sendmessage"</span><span style="color: #222222">)]</span>
<span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">static</span><span style="color: #222222"> </span><span style="color: #009695">async</span><span style="color: #222222"> </span><span style="color: #3363a4">Task</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">IActionResult</span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #222222">SendMessage</span><span style="color: #222222">(</span>
<span style="color: #222222">    </span><span style="color: #222222">[</span><span style="color: #222222">HttpTrigger</span><span style="color: #222222">(</span><span style="color: #222222">AuthorizationLevel</span><span style="color: #222222">.</span><span style="color: #222222">Anonymous</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"post"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"options"</span><span style="color: #222222">)]</span><span style="color: #3363a4">HttpRequest</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">,</span>
<span style="color: #222222">    </span><span style="color: #222222">[</span><span style="color: #222222">SignalR</span><span style="color: #222222">(</span><span style="color: #222222">HubName</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"testazuresignalrhub"</span><span style="color: #222222">)]</span><span style="color: #222222">IAsyncCollector</span><span style="color: #222222">&lt;</span><span style="color: #222222">SignalRMessage</span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #222222">signalRMessages</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #3363a4">ILogger</span><span style="color: #222222"> </span><span style="color: #222222">log</span><span style="color: #222222">)</span>
<span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #009695">try</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">!</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Credentials"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Credentials"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #db7100">"true"</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Origin"</span><span style="color: #222222">)</span><span style="color: #222222"> </span><span style="color: #222222">&amp;&amp;</span><span style="color: #222222"> </span><span style="color: #222222">!</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Origin"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Origin"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">[</span><span style="color: #db7100">"Origin"</span><span style="color: #222222">][</span><span style="color: #db7100">0</span><span style="color: #222222">]);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">ContainsKey</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Request-Headers"</span><span style="color: #222222">))</span>
<span style="color: #222222">        </span><span style="color: #222222">{</span>
<span style="color: #222222">            </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">HttpContext</span><span style="color: #222222">.</span><span style="color: #222222">Response</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">.</span><span style="color: #222222">Add</span><span style="color: #222222">(</span><span style="color: #db7100">"Access-Control-Allow-Headers"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Headers</span><span style="color: #222222">[</span><span style="color: #db7100">"access-control-request-headers"</span><span style="color: #222222">][</span><span style="color: #db7100">0</span><span style="color: #222222">]);</span>
<span style="color: #222222">        </span><span style="color: #222222">}</span>
<span style="color: #222222">        </span><span style="color: #888a85">// Commenting since not passing json payload. If passing json payload uncomment this line</span>
<span style="color: #222222">        </span><span style="color: #888a85">// var message = new JsonSerializer().Deserialize(new JsonTextReader(new StreamReader(req.Body)));</span>
<span style="color: #222222">        </span><span style="color: #009695">var</span><span style="color: #222222"> </span><span style="color: #222222">message</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">StreamReader</span><span style="color: #222222">(</span><span style="color: #222222">req</span><span style="color: #222222">.</span><span style="color: #222222">Body</span><span style="color: #222222">)</span><span style="color: #222222">.</span><span style="color: #222222">ReadToEnd</span><span style="color: #222222">();</span>
<span style="color: #222222">        </span><span style="color: #009695">await</span><span style="color: #222222"> </span><span style="color: #222222">signalRMessages</span><span style="color: #222222">.</span><span style="color: #222222">AddAsync</span><span style="color: #222222">(</span>
<span style="color: #222222">            </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #222222">SignalRMessage</span>
<span style="color: #222222">            </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #222222">Target</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #db7100">"sendMessage"</span><span style="color: #222222">,</span>
<span style="color: #222222">                </span><span style="color: #222222">Arguments</span><span style="color: #222222"> </span><span style="color: #222222">=</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222">[]</span><span style="color: #222222"> </span><span style="color: #222222">{</span><span style="color: #222222"> </span><span style="color: #222222">message</span><span style="color: #222222"> </span><span style="color: #222222">}</span>
<span style="color: #222222">            </span><span style="color: #222222">});</span>
<span style="color: #222222">        </span><span style="color: #222222">log</span><span style="color: #222222">.</span><span style="color: #222222">LogInformation</span><span style="color: #222222">(</span><span style="color: #db7100">"sendMessage API succeeded."</span><span style="color: #222222">);</span>
<span style="color: #222222">        </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">OkResult</span><span style="color: #222222">();</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">catch</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">BadRequestResult</span><span style="color: #222222">();</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span></span></code>

Now that Azure SignalR Service bindings are defined, update 'local.settings.json' and specify value for <code>"AzureSignalRConnectionString":"{AZURE_SIGNALR_CONNECTION_STRING}"</code> as this is needed to run host locally. You can start host by running command <code>func start --build<span class="Apple-converted-space">  </span>--debug VSCode<span class="Apple-converted-space">  </span>--cors "*"</code>.
<h2>Publish Docker Image to DockerHub</h2>
The next step is to create a Docker Hub repository. Update 'Dockerfile' environment variables based on your connection strings

<code>ENV AzureWebJobsStorage="{BLOB_STORAGE_CONNECTION_STRING}"
ENV AzureBlobStorage="{BLOB_STORAGE_CONNECTION_STRING_OF_CONTAINER_HAVING_TRIGGER}"
ENV AzureEventHubConnectionString="{EVENT_HUB_CONNECTION_STRING}"
ENV TestCosmonDBConnection="{COSMOS_DB_CONNECTION_STRING}"
ENV AzureSignalRConnectionString="{AZURE_SIGNALR_CONNECTION_STRING}"</code>

The commands to build Docker Image are listed below
<ul>
 	<li>Build Docker Image: <code>docker build -t samplefunc .</code></li>
 	<li>Tag Docker Image: <code>docker tag samplefunc {YOUR_DOCKER_REPO}</code></li>
 	<li>Publish Image to Docker Hub: <code>docker push {YOUR_DOCKER_REPO}</code></li>
</ul>
<h2>Create Function App from Docker Image in Azure Portal</h2>
<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-2.44.09-PM.png"></a>Create a new Function App in Azure Portal and specify
<ul>
 	<li>App name</li>
 	<li>Resource group</li>
 	<li>Select OS as 'Linux (Preview)'</li>
 	<li>Select Publish as 'Docker Image'</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-2.48.26-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-2.48.26-PM-1024x718.png" alt="" width="645" height="452" class="alignnone wp-image-1195" /></a>

Docker image published to Docker Hub in previous step will be used to configure container. Open 'Configure container' and select 'Docker Hub' tab and specify values for image and option tag along with repository access.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-2.44.09-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-2.44.09-PM-1024x575.png" alt="" width="644" height="362" class="alignnone wp-image-1205" /></a>

After function App is created, you will see list of Functions. Navigate to the Overview tab and keep a note of the URL.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-5.50.52-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-5.50.52-PM-1024x730.png" alt="" width="663" height="473" class="alignnone wp-image-1245" /></a>

The function URL will be used by SignalR client to connect and send/receive events. If you want to connect to this function app from a browser based client, you will need to allow the origin to make cross-origin calls in <code>Platform Features &gt; API &gt; CORS </code> e.g. I have allowed 'https://localhost:4200' and 'https://localhost:4200/home' origins.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-1.41.18-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-29-at-1.41.18-PM-1024x922.png" alt="" width="520" height="467" class="alignnone wp-image-1215" /></a>
<h2>Deploy functions to Azure Kubernetes Service from VS Code</h2>
You can also deploy Azure functions 2.0 to AKS and command is <code>func deploy --platform kubernetes --name {DockerHubRepositoryName} --registry {DockerHubRegistry}</code>.  You need to update placeholders with your Docker hub repository name and account. Once deployment is complete, you can open the endpoint to verify the deployment. You can also open AKS dashboard to view deployed resources under namespace 'azure-functions'.
<h2>Summary</h2>
In the next article of this series I will create a few client apps (Console/Angular) to integrate with Azure SignalR Service, ASP.NET SignalR and Azure SignalR Service bindings for Azure functions. This completes this article and complete<span> source code can be downloaded from <a target="_blank" href="https://github.com/atverma/azurekubernetesservice-azurefunctions-2.0" rel="noopener">GitHub</a>.</span>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>ASP.NET Core 2.1 Web API - Load App Configuration from appsettings.json, Dockerfile environment variables, Azure Key Vault Secrets and Kubernetes ConfigMaps/Secrets</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/asp-net-core-2-1-web-api-load-app-configuration-from-appsettings-json-dockerfile-environment-variables-azure-key-vault-secrets-and-kubernetes-configmaps-secrets</link>
      <pubDate>Fri, 19 Oct 2018 22:25:06 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=1055</guid>
      <description><![CDATA[This article is second part of the series on Deploying Angular, ASP.NET Core and SQL Server on Linux...]]></description>
      <content:encoded><![CDATA[This article is second part of the series on Deploying Angular, ASP.NET Core and SQL Server on Linux to Azure Kubernetes Service (AKS) cluster. The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/16/azure-kubernetes-service-aks-deploying-angular-asp-net-core-and-sql-server-on-linux/" rel="noopener">first part</a>, describes steps needed to deploy these components to AKS. <span>App configuration in ASP.NET Core is based on key-value pairs established by </span>configuration providers<span>. Configuration providers read configuration data into key-value pairs from a variety of configuration sources.</span> In this article I am going to share multiple ways to load App configuration in ASP.net Core Web API
<ul>
 	<li>Hosting Environment specific appsettings.json</li>
 	<li>Dockerfile Environment Variables</li>
 	<li>Kubernetes
<ul>
 	<li>Container Environment variables with data from ConfigMap/Secret</li>
 	<li>Populate Volume (Config file) with data stored in a ConfigMap/Secret</li>
</ul>
</li>
 	<li>Azure Key Vault Secrets</li>
</ul>
<span>The tools used to develop these components are Visual Studio for Mac/VS Code/VS 2017, AKS Dashboard, Docker for Desktop and kubectl. The formatting of code snippets in this article may get distorted (especially yaml), thus please refer to </span><a target="_blank" href="https://github.com/atverma/AKS-Angular-AspnetCore-SQLServer" rel="noopener">GitHub repository</a><span> for complete source code for this article.</span>

I have extended sample solution of first part of the series by adding new files i.e. ConfigController, Kubernetes_ConfigMap.yaml and Kubernetes_Deployment_V2.yaml

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-19-at-2.33.02-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-19-at-2.33.02-PM.png" alt="" width="262" height="450" class="alignnone wp-image-1075" /></a>
<h2>Hosting environment specific appsettings.json</h2>
Hosting environment specific versions of appsettings.json is one of the ways to define App configuration in ASP.net core. In <code>ConfigureAppConfiguration</code> method, based on Hosting environment, App Config will be loaded as displayed below

<code><span> <span style="color: #222222">config</span><span style="color: #222222">.</span><span style="color: #222222">AddJsonFile</span><span style="color: #222222">(</span><span style="color: #db7100">"appsettings.json"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">optional</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">false</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">reloadOnChange</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">true</span><span style="color: #222222">)</span>
<span style="color: #222222">                        </span><span style="color: #222222">.</span><span style="color: #222222">AddJsonFile</span><span style="color: #222222">(</span><span style="color: #db7100">$"appsettings.</span><span style="color: #222222">{</span><span style="color: #222222">env</span><span style="color: #222222">.</span><span style="color: #222222">EnvironmentName</span><span style="color: #222222">}</span><span style="color: #db7100">.json"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">optional</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">false</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">reloadOnChange</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">true</span><span style="color: #222222">)</span></span></code>

I have defined a few configuration settings in appsettings.json<code>
<span> <span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #3363a4">"AppConfiguration"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"DatabaseConnectionString"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"Server=mssql-sample-service,1433;Database=UsersDB;User Id=sa;Password=P@ssword1$;"</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"IsVaultEnabled"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">false</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"Vault"</span><span style="color: #222222">:</span><span style="color: #222222">  </span><span style="color: #db7100">"AZURE_KEY_VAULT_DNS_NAME"</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"ClientId"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"APPLICATION_ID"</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"ClientSecret"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"APPLICATION_KEY"</span><span style="color: #222222">,</span>
<span style="color: #222222">        </span><span style="color: #3363a4">"DatabaseConnectionStringFromAppsettings"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"DatabaseConnectionStringFromAppsettingsValue"</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span></span></code>

The Production environment file i.e. appsettings.Production.json also defines <code><span><span style="color: #3363a4">DatabaseConnectionStringFromAppsettings</span></span></code> setting, thus during Production Environment build, configuration from this file will get applied based on precedence.

<code><span><span style="color: #222222">{</span>
<span style="color: #222222">  </span><span style="color: #3363a4">"AppConfiguration"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #3363a4">"DatabaseConnectionStringFromAppsettings"</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #db7100">"Prod:DatabaseConnectionStringFromAppsettingsValue"</span>
<span style="color: #222222">  </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span>
</span></code>
<h2>Dockerfile Environment Variable</h2>
Environment variables can be defined in Dockerfile and args can be passed during building Docker image. In <code>ConfigureAppConfiguration</code> method, <code>config.AddEnvironmentVariables()</code> loads environment variables. In DockerFile displayed below, argument <code>ARG dbConnectionString</code> is defined along with environment variable <code>ENV DatabaseConnectionString</code>. This ARG value can then be specified when building Docker image and the command is <code>docker build --build-arg dbConnectionString=DatabaseConnectionStringFromDockerEnvVariable -t testappwebapi .</code>

<code>
<span> <span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">microsoft/dotnet:2.1-aspnetcore-runtime AS base</span>
<span style="color: #009695">WORKDIR</span><span style="color: #222222"> </span><span style="color: #222222">/app</span>
<span style="color: #009695">EXPOSE</span><span style="color: #222222"> </span><span style="color: #222222">80</span>
<span style="color: #009695">ARG</span><span style="color: #222222"> </span><span style="color: #222222">dbConnectionString</span>
<span style="color: #009695">ENV</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromDockerEnvVariable $dbConnectionString</span>
<span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">microsoft/dotnet:2.1-sdk AS build</span>
<span style="color: #009695">WORKDIR</span><span style="color: #222222"> </span><span style="color: #222222">/src</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">[</span><span style="color: #db7100">"</span><span style="color: #db7100">SampleWebApp/SampleWebApp.csproj</span><span style="color: #db7100">"</span><span style="color: #222222">, </span><span style="color: #db7100">"</span><span style="color: #db7100">.</span><span style="color: #db7100">"</span><span style="color: #222222">]</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">dotnet restore </span><span style="color: #db7100">"</span><span style="color: #db7100">SampleWebApp.csproj</span><span style="color: #db7100">"</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">. .</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">dotnet build </span><span style="color: #db7100">"</span><span style="color: #db7100">SampleWebApp.csproj</span><span style="color: #db7100">"</span><span style="color: #222222"> -c Release -o /app</span>
<span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">build AS publish</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">dotnet publish </span><span style="color: #db7100">"</span><span style="color: #db7100">SampleWebApp.csproj</span><span style="color: #db7100">"</span><span style="color: #222222"> -c Release -o /app</span>
<span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">base AS final</span>
<span style="color: #009695">WORKDIR</span><span style="color: #222222"> </span><span style="color: #222222">/app</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">--from=publish /app .</span>
<span style="color: #009695">ENTRYPOINT</span><span style="color: #222222"> </span><span style="color: #222222">[</span><span style="color: #db7100">"</span><span style="color: #db7100">dotnet</span><span style="color: #db7100">"</span><span style="color: #222222">, </span><span style="color: #db7100">"</span><span style="color: #db7100">SampleWebApp.dll</span><span style="color: #db7100">"</span><span style="color: #222222">]</span></span>
</code>
<h2>ConfigMaps and Secrets in Kubernetes</h2>
<span>ConfigMaps and Secret allows you to decouple configuration artifacts from image content to keep containerized applications portable. For this article, I will show how to use ConfigMap to define app configuration.</span>
<h3>Container Environment variables with data from ConfigMap/Secret</h3>
The ConfigMap resource displayed below defines <code><span><span style="color: #3363a4">DatabaseConnectionStringFromKubernetesEnvVariable</span></span></code> setting.

<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">DatabaseConnectionStringFromKubernetesEnvVariable</span><span style="color: #222222">:</span><span style="color: #db7100"> DatabaseConnectionStringFromKubernetesEnvVariableValue</span></span></code>

Container environment variables can be set from Secret using <code>env[].valueFrom.secretKeyRef</code> and from ConfigMap using <code>env[].valueFrom.configMapKeyRef</code>. Container environment variable defined in deployment resource(Kubernetes_Deployment_V2.yaml) will be set from ConfigMap and the partial yaml snippet is

<code><span><span style="color: #3363a4">env</span><span style="color: #222222">:</span>
<span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> DatabaseConnectionStringFromKubernetesEnvVariable</span>
<span style="color: #222222">  </span><span style="color: #3363a4">valueFrom</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">configMapKeyRef</span><span style="color: #222222">:</span>
<span style="color: #222222">       </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-1</span>
<span style="color: #222222">       </span><span style="color: #3363a4">key</span><span style="color: #222222">:</span><span style="color: #db7100"> DatabaseConnectionStringFromKubernetesEnvVariable</span></span></code>
<h3>Populate Volume (Config file) with data stored in a ConfigMap/Secret</h3>
This is similar to previous step however in this case, data stored in ConfigMap will be mounted to a json file. ASP.net core web api will load App Configuration from this json file. The ConfigMap resource displayed below defines data for <code><span><span style="color: #3363a4">AppConfig.json</span><span style="color: #222222">:</span></span></code>

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-2</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">AppConfig.json</span><span style="color: #222222">:</span><span style="color: #db7100"> |-</span>
<span style="color: #222222">    {</span>
<span style="color: #222222">        "AppConfiguration": {</span>
<span style="color: #222222">            "<span> DatabaseConnectionStringFromKubernetesMountedFile</span>": "<span>DatabaseConnectionStringFromKubernetesMountedFileValue</span>"</span></span><span>
<span style="color: #222222">        }</span>
<span style="color: #222222">    }</span></span></code>

<code><span><span style="color: #3363a4">AppConfig.json</span></span></code> file will be mounted from ConfigMap in deployment resource(Kubernetes_Deployment_V2.yaml). The partial yaml snippets where <code><span><span style="color: #3363a4">AppConfig.json</span></span></code> is mounted to path <code><span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /app/AppConfig.json</span></span></code> from <code><span><span style="color: #db7100">samplewebapp-configmap-2</span></span></code> ConfigMap is

<code><span> <span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-2</span>
<span style="color: #222222">  </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /app/AppConfig.json</span>
<span style="color: #222222">  </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> AppConfig.json</span></span></code>

<code><span> <span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-2</span>
<span style="color: #222222">  </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> samplewebapp-configmap-2</span></span></code>

In <code>ConfigureAppConfiguration</code> method, App configuration is loaded from <code><span><span style="color: #3363a4">AppConfig.json</span></span></code>

<code><span><span style="color: #222222">config.</span><span style="color: #222222">AddJsonFile</span><span style="color: #222222">(</span><span style="color: #db7100">"/app/AppConfig.json"</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">optional</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">false</span><span style="color: #222222">,</span><span style="color: #222222"> </span><span style="color: #222222">reloadOnChange</span><span style="color: #222222">:</span><span style="color: #222222"> </span><span style="color: #009695">true</span></span></code>
<h2>Azure Key Vault Secrets</h2>
<span>Azure Key Vault can be used to securely store and tightly control access to tokens, passwords, certificates, API keys, and other secrets. Centralizing storage of application secrets in Azure Key Vault allows you to control their distribution. Key Vault greatly reduces the chances that secrets may be accidentally leaked. I will provide the steps needed to load App Configuration from Azure Key Vault secrets.</span>
<h3>Create Azure AD Application</h3>
<span>For Service-to-Azure-Service authentication, this approach involves creating an Azure AD application and associated credential, and using that credential to get a token. This approach does has short comings i.e. application credentials need to be specified in code and credentials expiry which Managed Service Identity fixes. Azure Kubernetes Service(AKS) is not currently natively integrated with Azure Key Vault however, the <a href="https://github.com/Azure/kubernetes-keyvault-flexvol">Azure Key Vault FlexVolume for Kubernetes project</a> enables direct integration from Kubernetes pods to KeyVault secrets.</span>

Navigate to <code>Azure AD &gt; App Registrations &gt; New Application Registration</code> and select Application Type as Web app/API and specify Name and Sign-on URL

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.54.34-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.54.34-PM.png" alt="" width="353" height="313" class="alignnone wp-image-1095" /></a>

After Azure AD Application is created, navigate to the resource and take note of Application ID which is needed to be specified for ClientID in ASP.net Core Web API appsettings.json.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.57.54-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.57.54-PM-1024x508.png" alt="" width="450" height="223" class="alignnone wp-image-1105" /></a>

Open Settings and create a new Key. Take note of the value (you can only copy after saving) which is needed to be specified for ClientSecret in ASP.net Core Web API appsettings.json.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-10.14.52-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-10.14.52-PM-1024x250.png" alt="" width="664" height="162" class="alignnone wp-image-1115" /></a>
<h3>Create Azure Key Vault</h3>
Create a key vault resource using Azure CLI or Portal

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.42.53-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.42.53-PM-645x1024.png" alt="" width="345" height="548" class="alignnone wp-image-1085" /></a>

You need to specify Access policies. Select principal as Application you created in previous step and grant permissions e.g. I have granted Read and List Secret permissions.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.55.58-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-18-at-9.55.58-PM-1024x760.png" alt="" width="539" height="400" class="alignnone wp-image-1125" /></a>

Create a Secret and specify Name and Value. The value for this secret is going to be loaded in App Configuration.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-19-at-5.46.57-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-19-at-5.46.57-PM-1024x790.png" alt="" width="446" height="344" class="alignnone wp-image-1135" /></a>

Keep note of the Key Vault's DNS name which is needed to be specified for Vault in ASP.net Core Web API appsettings.json.
<h3>ASP.net Core Web API</h3>
Add Azure Key Vault Configuration provider nuget package <code>Microsoft.Extensions.Configuration.AzureKeyVault</code>.

In <code>ConfigureAppConfiguration</code> method snippet displayed below, App configuration is loaded from Azure Key Vault. As described above, you need to specify values for Vault, ClientId and ClientSecret in appsettings.json. Along with this you need to set <code><span><span style="color: #222222">IsVaultEnabled</span></span></code> to true in appsettings.json.

<code><span><span style="color: #009695">if</span><span style="color: #222222"> </span><span style="color: #222222">(</span><span style="color: #3363a4">Convert</span><span style="color: #222222">.</span><span style="color: #222222">ToBoolean</span><span style="color: #222222">(</span><span style="color: #222222">configInProgress</span><span style="color: #222222">[</span><span style="color: #db7100">"AppConfiguration:IsVaultEnabled"</span><span style="color: #222222">]))</span>
<span style="color: #222222">{</span>
<span style="color: #222222">  </span><span style="color: #222222">config</span><span style="color: #222222">.</span><span style="color: #222222">AddAzureKeyVault</span><span style="color: #222222">(</span><span style="color: #222222">configInProgress</span><span style="color: #222222">[</span><span style="color: #db7100">"AppConfiguration:Vault"</span><span style="color: #222222">],</span>
<span style="color: #222222">                          </span><span style="color: #222222">configInProgress</span><span style="color: #222222">[</span><span style="color: #db7100">"AppConfiguration:ClientId"</span><span style="color: #222222">],</span>
<span style="color: #222222">                          </span><span style="color: #222222">configInProgress</span><span style="color: #222222">[</span><span style="color: #db7100">"AppConfiguration:ClientSecret"</span><span style="color: #222222">]);</span></span>
<span><span style="color: #222222"></span></span><span style="color: #222222"> </span><span style="color: #222222">config</span><span style="color: #222222">.</span><span style="color: #222222">Build</span><span style="color: #222222">();</span>
<span style="color: #222222">}</span></code>

In ConfigureServices method of Startup class, Configuration instance is registered with AppConfiguration class

<code><span> <span style="color: #222222">services</span><span style="color: #222222">.</span><span style="color: #222222">Configure</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">AppConfiguration</span><span style="color: #222222">&gt;(</span><span style="color: #222222">Configuration</span><span style="color: #222222">.</span><span style="color: #222222">GetSection</span><span style="color: #222222">(</span><span style="color: #db7100">"AppConfiguration"</span><span style="color: #222222">));</span>
<span style="color: #222222">services</span><span style="color: #222222">.</span><span style="color: #222222">Configure</span><span style="color: #222222">&lt;</span><span style="color: #3363a4">AppConfiguration</span><span style="color: #222222">&gt;(</span><span style="color: #222222">Configuration</span><span style="color: #222222">);</span></span></code>

<code><span> <span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">class</span><span style="color: #222222"> </span><span style="color: #3363a4">AppConfiguration</span>
<span style="color: #222222">{</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionString</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">bool</span><span style="color: #222222"> </span><span style="color: #222222">IsVaultEnabled</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">Vault</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">ClientId</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">ClientSecret</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromAppsettings</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromDockerEnvVariable</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromKubernetesEnvVariable</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromKubernetesMountedFile</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">    </span><span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #009695">string</span><span style="color: #222222"> </span><span style="color: #222222">DatabaseConnectionStringFromAzureKeyVault</span>
<span style="color: #222222">    </span><span style="color: #222222">{</span>
<span style="color: #222222">        </span><span style="color: #009695">get</span><span style="color: #222222">;</span>
<span style="color: #222222">        </span><span style="color: #009695">set</span><span style="color: #222222">;</span>
<span style="color: #222222">    </span><span style="color: #222222">}</span>
<span style="color: #222222">}</span></span></code>

The code snippet from ConfigController displays App Configuration values from all the sources

<code><span> <span style="color: #222222">[</span><span style="color: #3363a4">HttpGet</span><span style="color: #222222">]</span>
<span style="color: #009695">public</span><span style="color: #222222"> </span><span style="color: #3363a4">IEnumerable</span><span style="color: #222222">&lt;</span><span style="color: #009695">string</span><span style="color: #222222">&gt;</span><span style="color: #222222"> </span><span style="color: #222222">Get</span><span style="color: #222222">()</span>
<span style="color: #222222">{</span>
<span style="color: #222222">     </span><span style="color: #009695">return</span><span style="color: #222222"> </span><span style="color: #009695">new</span><span style="color: #222222"> </span><span style="color: #3363a4">List</span><span style="color: #222222">&lt;</span><span style="color: #009695">string</span><span style="color: #222222">&gt;</span><span style="color: #222222">()</span>
<span style="color: #222222">            </span><span style="color: #222222">{</span>
<span style="color: #222222">                </span><span style="color: #db7100">$"DatabaseConnectionStringFromAppsettings: </span><span style="color: #222222">{</span><span style="color: #222222">_appSettings</span><span style="color: #222222">.</span><span style="color: #222222">Value</span><span style="color: #222222">.</span><span style="color: #222222">DatabaseConnectionStringFromAppsettings</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">,</span>
<span style="color: #222222">                </span><span style="color: #db7100">$"DatabaseConnectionStringFromDockerEnvVariable: </span><span style="color: #222222">{</span><span style="color: #222222">_appSettings</span><span style="color: #222222">.</span><span style="color: #222222">Value</span><span style="color: #222222">.</span><span style="color: #222222">DatabaseConnectionStringFromDockerEnvVariable</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">,</span>
<span style="color: #222222">                </span><span style="color: #db7100">$"DatabaseConnectionStringFromKubernetesEnvVariable: </span><span style="color: #222222">{</span><span style="color: #222222">_appSettings</span><span style="color: #222222">.</span><span style="color: #222222">Value</span><span style="color: #222222">.</span><span style="color: #222222">DatabaseConnectionStringFromKubernetesEnvVariable</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">,</span>
<span style="color: #222222">                </span><span style="color: #db7100">$"DatabaseConnectionStringFromKubernetesMountedFile: </span><span style="color: #222222">{</span><span style="color: #222222">_appSettings</span><span style="color: #222222">.</span><span style="color: #222222">Value</span><span style="color: #222222">.</span><span style="color: #222222">DatabaseConnectionStringFromKubernetesMountedFile</span><span style="color: #222222">}</span><span style="color: #db7100">"</span><span style="color: #222222">,</span>
<span style="color: #222222">                </span><span style="color: #db7100">$"DatabaseConnectionStringFromAzureKeyVault: </span><span style="color: #222222">{</span><span style="color: #222222">_appSettings</span><span style="color: #222222">.</span><span style="color: #222222">Value</span><span style="color: #222222">.</span><span style="color: #222222">DatabaseConnectionStringFromAzureKeyVault</span><span style="color: #222222">}</span><span style="color: #db7100">"</span>
<span style="color: #222222">            </span><span style="color: #222222">};</span>
<span style="color: #222222">}</span></span></code>

Build the docker image and deploy the resources to Azure Kubernetes Service. Browse to "https://YOUR_HOST/api/Config" to see list of App Configurations with Values populated from these sources.

The source code for this article can be downloaded from <span> </span><a target="_blank" href="https://github.com/atverma/AKS-Angular-AspnetCore-SQLServer" rel="noopener">GitHub repository</a>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Ingest Azure Redis Cache messages into Elasticsearch, Logstash and Kibana cluster deployed in Azure Kubernetes Service (AKS)</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/ingest-azure-redis-cache-messages-into-elasticsearch-logstash-and-kibana-cluster-deployed-in-azure-kubernetes-service-aks</link>
      <pubDate>Sun, 14 Oct 2018 22:43:14 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=935</guid>
      <description><![CDATA[This is third article on the series on deploying Elasticsearch, Logstash and Kibana (ELK) in Azure...]]></description>
      <content:encoded><![CDATA[This is third article on the series on deploying Elasticsearch, Logstash and Kibana (ELK) in Azure Kubernetes Service (AKS) cluster. The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/24/azure-kubernetes-service-aks-deploying-elasticsearch-logstash-and-kibana-elk-and-consume-messages-from-azure-event-hub/" rel="noopener">first article</a> covered deploying non-SSL ELK to AKS and consuming messages from Azure Event Hub. The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/10/09/azure-kubernetes-service-aks-azure-ad-saml-based-single-sign-on-to-secure-elasticsearch-and-kibana-and-securing-communications-in-elk/" rel="noopener">second article</a> described how to secure communications in ELK and use Azure AD SAML based SSO for Kibana and Elasticsearch. In this article I am going to share steps needed to ingest Azure Redis Cache messages into Elasticsearch using Logstash's Redis plugin.

<span>Azure Redis Cache is based on the popular open-source </span><a href="https://redis.io/">Redis cache</a><span>. It is typically used as a cache to improve the performance and scalability of systems that rely heavily on backend data-stores. </span>Logstash's Redis plugin will read events from Redis instance. <span>I will create a Logstash event processing pipeline where I will define Redis as input and Elasticsearch as output. The component diagram has been updated to add Azure Redis Cache integration.</span><a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-6.10.13-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-6.10.13-PM-1024x565.png" alt="" width="786" height="434" class="alignnone wp-image-1005" /></a>

<span>The dev tools used to develop these components are Visual Studio for Mac/VS Code, AKS Dashboard, kubectl, bash and openssl. The code snippets in this article are mostly yaml snippets and are included for reference only as formatting may get distorted thus please refer to </span><a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO" rel="noopener">GitHub repository</a><span> for formatted resources.</span>
<h2>Create Azure Redis Cache</h2>
Create Azure Redis Cache using Portal or Azure CLI command <code>az redis create</code>

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-11.49.57-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-11.49.57-AM-516x1024.png" alt="" width="331" height="657" class="alignnone wp-image-945" /></a>

Navigate to this resource and keep note of the settings listed below as you are going to need them in subsequent sections
<ol>
 	<li><strong>Host Name:</strong> You will need to specify Host name is Logstash pipeline.</li>
 	<li><strong>Secondary Access Key:</strong> You will need to specify password in Logstash pipeline.</li>
 	<li><strong>StackExchange.Redis connection string:</strong> You will need to specify connection string in <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/AzureRedisCacheSample" rel="noopener">AzureRedisCacheSample</a> so as to publish messages to Redis.</li>
 	<li><strong>Port:</strong> Default non-SSL port is 6379. By default, non-SSL port is disabled thus the port to which Logstash pipeline will connect is 6380.</li>
</ol>
<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-11.54.44-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-11.54.44-AM-1024x470.png" alt="" width="473" height="217" class="alignnone wp-image-955" /></a>
<h2>Logstash Redis input plugin</h2>
<span>As mentioned previously, I will create a Logstash pipeline which will use Logstash Redis input plugin. This input will read events from a Redis instance; it supports both Redis channels and lists. </span>You can read more about <a target="_blank" href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-redis.html" rel="noopener">Redis input plugin</a>. You can get installed plugins list by following steps listed below
<ul>
 	<li>Run command<span> </span><code>kubectl exec -ti {Logstash_Pod_Name}  bash</code> to connect to Logstash POD.</li>
 	<li>Run command<span> </span><code>bin/logstash-plugin list</code> to see installed plugins</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-5.34.40-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-5.34.40-PM.png" alt="" width="283" height="259" class="wp-image-985 aligncenter" /></a>
<h2>Deploy Logstash to Azure Kubernetes Service</h2>
<span>Logstash is data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to Elasticsearch. </span>Logstash will use Azure Event Hub plugin and Redis input plugin to ingest data into Elasticsearch. In this article, I am going to share<span> main pointers about changes needed to Logstash resources i.e. ConfigMap and Deployment in order to subscribe to Azure Redis Cache only. You can refer to first two parts of this series for more details.</span>

The steps needed to deploy Logstash to AKS are listed below
<h3>Create a Kubernetes ConfigMap</h3>
Create a new pipeline in Logstash for Azure Redis cache integration and <span>main pointers about changes needed to this resource as compared to earlier parts of this series are</span>
<ul>
 	<li>A new pipeline has been defined i.e. <code>azureredis</code> is the pipeline id and <code>azureredis.cfg</code> is the path of the configuration file for Redis cache integration. <code>azureredis.cfg</code> f<span>ile will be mounted from ConfigMap. The Logstash event processing pipeline has three stages: inputs → filters → outputs. This file defines the logstash pipeline for Redis Cache.</span>
<ul>
 	<li>Specify <code>host =&gt; "{YOUR_REDIS_HOST_NAME}"</code> based on your Redis instance host name</li>
 	<li>The sample Redis client is publishing messages to channel, thus data_type is channel <code>data_type =&gt; "channel"</code></li>
 	<li>The channel name I have specified is 'messages'. Update channel name <code>key =&gt; "messages"</code> based on your specified channel name.</li>
 	<li>Specify password as per your Redis Cache secondary key value <code>password =&gt; "{YOUR_REDIS_SECONDARY_KEY}"</code></li>
 	<li>Since HTTPS is enabled, I have specified port as 6380 <code>port =&gt; 6380</code>. The default setting is non-SSL port i.e. 6379.</li>
 	<li>SSL is enabled <code>ssl =&gt; true</code></li>
 	<li>Output is Elasticsearch and Index name is defined as<span> </span><code>index =&gt; "azureredis-%{+YYYY.MM.dd}</code></li>
</ul>
</li>
</ul>
<span>The yaml snippet to create this resource is displayed below</span>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.url</span><span style="color: #222222">:</span><span style="color: #db7100"> https://sample-elasticsearch:9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">dead_letter_queue.enable</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.username</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash_system </span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.ssl.ca</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">pipelines.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">pipeline.id</span><span style="color: #222222">:</span><span style="color: #db7100"> azureeventhubs</span>
<span style="color: #222222">      </span><span style="color: #3363a4">path.config</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/azureeventhubs.cfg"</span>
<span style="color: #222222">    </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">pipeline.id</span><span style="color: #222222">:</span><span style="color: #db7100"> azureredis</span>
<span style="color: #222222">      </span><span style="color: #3363a4">path.config</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/azureredis.cfg"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">azureeventhubs.cfg</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    input {</span>
<span style="color: #222222">      azure_event_hubs {</span>
<span style="color: #222222">        event_hub_connections =&gt; ["{AZURE_EVENT_HUB_CONNECTION_STRING};EntityPath=logstash"]</span>
<span style="color: #222222">        threads =&gt; 2</span>
<span style="color: #222222">        decorate_events =&gt; true</span>
<span style="color: #222222">        consumer_group =&gt; "$Default"</span>
<span style="color: #222222">        storage_connection =&gt; "{STORAGE_ACCOUNT_CONNECTION_STRING}"</span>
<span style="color: #222222">        storage_container =&gt; "logstash"</span>
<span style="color: #222222">        }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    filter {</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    output {</span>
<span style="color: #222222">      elasticsearch {</span>
<span style="color: #222222">        hosts =&gt; ["sample-elasticsearch:9200" ]</span>
<span style="color: #222222">        user =&gt; "elastic"</span>
<span style="color: #222222">        password =&gt; "Password1$"</span>
<span style="color: #222222">        index =&gt; "azureeventhub-%{+YYYY.MM.dd}"</span>
<span style="color: #222222">        ssl =&gt; true</span>
<span style="color: #222222">        cacert =&gt; "/usr/share/logstash/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">      }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">  </span><span style="color: #3363a4">azureredis.cfg</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    input {</span>
<span style="color: #222222">      redis {</span>
<span style="color: #222222">        host =&gt; "{YOUR_REDIS_HOST_NAME}"</span>
<span style="color: #222222">        key =&gt; "messages"</span>
<span style="color: #222222">        data_type =&gt; "channel"</span>
<span style="color: #222222">        password =&gt; "{YOUR_REDIS_SECONDARY_KEY}"</span>
<span style="color: #222222">        port =&gt; 6380</span>
<span style="color: #222222">        ssl =&gt; true</span>
<span style="color: #222222">     }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    filter {</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    output {</span>
<span style="color: #222222">      elasticsearch {</span>
<span style="color: #222222">        hosts =&gt; ["sample-elasticsearch:9200" ]</span>
<span style="color: #222222">        user =&gt; "elastic"</span>
<span style="color: #222222">        password =&gt; "Password1$"</span>
<span style="color: #222222">        index =&gt; "azureredis-%{+YYYY.MM.dd}"</span>
<span style="color: #222222">        ssl =&gt; true</span>
<span style="color: #222222">        cacert =&gt; "/usr/share/logstash/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">      }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.conf</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span></span></code>
<h3>Create a Kubernetes Deployment</h3>
The only change needed to <span>Logstash_Deployment.yaml is </span>to mount the new pipeline configuration for Redis i.e. <code>azureredis.cfg</code>. You can refer to previous articles of this series for further details about this resource.

<code>volumeMounts:
- name: sample-logstash-configmap
mountPath: /usr/share/logstash/azureredis.cfg
subPath: azureredis.cfg</code>
<h2>Deploy Logstash resources to AKS</h2>
Deploy Logstash resources to AKS. Once deployed run <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/AzureRedisCacheSample" rel="noopener">AzureRedisCacheSample</a> to publish messages to Redis. This is a sample client (.NET Core 2.1) to send messages to Redis channel. This sample also subscribes to Redis channel locally and prints the messages. You need to update StackExchange.Redis connection string and channel name.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-6.37.22-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-6.37.22-PM.png" alt="" width="431" height="210" class="alignnone wp-image-1015" /></a>

After sending a few messages, navigate to Kibana endpoint and you will see events received and events emitted in monitoring section

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-5.56.49-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-5.56.49-PM-1024x212.png" alt="" width="767" height="159" class="alignnone wp-image-995" /></a>

You can also create an Index filter in Kibana and messages sent to Redis will be displayed

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-1.29.00-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-14-at-1.29.00-PM-1024x438.png" alt="" width="703" height="301" class="alignnone wp-image-965" /></a>

&nbsp;

This completes the article on ingesting Azure Redis Cache messages into Elasticsearch using Logstash's Redis plugin. The sample code for this article can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO/Logstash" rel="noopener">GitHub</a>.

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Azure Kubernetes Service (AKS): Azure AD SAML based Single Sign on to secure Elasticsearch and Kibana and securing communications in ELK</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-kubernetes-service-aks-azure-ad-saml-based-single-sign-on-to-secure-elasticsearch-and-kibana-and-securing-communications-in-elk</link>
      <pubDate>Tue, 09 Oct 2018 05:52:34 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=825</guid>
      <description><![CDATA[This is second part of the series on deploying Elasticsearch, Logstash and Kibana (ELK) to Azure...]]></description>
      <content:encoded><![CDATA[This is second part of the series on deploying Elasticsearch, Logstash and Kibana (ELK) to Azure Kubernetes Service cluster. In this article I am going to share steps needed to enable Azure AD SAML based single sign on to secure Elasticsearch and Kibana hosted in AKS. I will also go through steps needed to secure communications in ELK cluster. The first part describes steps needed to deploy ELK to AKS and consume messages from Azure Event Hub

https://blogs.msdn.microsoft.com/atverma/2018/09/24/azure-kubernetes-service-aks-deploying-elasticsearch-logstash-and-kibana-elk-and-consume-messages-from-azure-event-hub/

The third part describes steps needed <span>to ingest Azure Redis Cache messages into Elasticsearch using Logstash's Redis plugin.</span>
https://blogs.msdn.microsoft.com/atverma/2018/10/14/ingest-azure-redis-cache-messages-into-elasticsearch-logstash-and-kibana-cluster-deployed-in-azure-kubernetes-service-aks

<span>Using SAML SSO for Elasticsearch with AAD means that Elasticsearch does not need to be seeded with any user accounts from the directory. Instead, Elasticsearch is able to rely on the claims sent within a SAML token in response to successful authentication to determine identity and privileges. I have referred to this <a target="_blank" href="https://www.elastic.co/blog/saml-based-single-sign-on-with-elasticsearch-and-azure-active-directory" rel="noopener">article</a> to enable SAML based single sign on for Elasticsearch.</span>

<span>Kibana, as the user facing component, interacts with the user’s browser and receives all the SAML messages that the Azure AD sends to the Elastic Stack Service Provider. Elasticsearch implements most of the functionality a SAML Service Provider needs. It holds all SAML related configuration in the form of an authentication realm and it also generates all SAML messages required and passes them to Kibana to be relayed to the user’s browser. It finally consumes all SAML Responses that Kibana relays to it, verifies them, extracts the necessary authentication information and creates the internal authentication tokens based on that.</span> The component diagram has been updated to add Azure AD SAML based SSO integration.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-06-at-10.18.17-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-06-at-10.18.17-PM-1024x562.png" alt="" width="782" height="429" class="alignnone wp-image-835" /></a>

<span>The dev tools used to develop these components are Visual Studio for Mac/VS Code, AKS Dashboard, kubectl, bash and openssl. The code snippets in this article are mostly yaml snippets and are included for reference only as formatting may get distorted thus please refer to <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO" rel="noopener">GitHub repository</a> for formatted resources.</span>
<h2>Azure AD - Create an Enterprise Application</h2>
<span>Before any messages can be exchanged between an Identity Provider and a Service Provider, one needs to know the configuration details and capabilities of the other. This configuration and the capabilities are encoded in an XML document, that is the SAML Metadata of the SAML entity. Thus </span>we need to create non-gallery enterprise application within Azure AD. Please refer to these documents at <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-single-sign-on-non-gallery-applications" rel="noopener">msdn</a> and <a target="_blank" href="https://www.elastic.co/blog/saml-based-single-sign-on-with-elasticsearch-and-azure-active-directory" rel="noopener">elastic</a> for more details as I am going to share main pointers as listed below
<ul>
 	<li><span>In Azure Portal, navigate to </span><code>Azure Active Directory &gt; Enterprise Applications &gt; New application &gt; Non-gallery application</code> and create an Application.</li>
 	<li>Configure Single Sign on and specify Identifier and Reply URL. You need to update KIBANA_URL placeholder with DNS of Kibana as displayed below based on your Kibana endpoint. Please note that Reply URL needs to be an HTTPS endpoint. I will also cover securing ELK cluster in this article where I am going to share steps needed to enable TLS for Kibana.<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-1.30.44-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-1.30.44-PM-1024x363.png" alt="" width="594" height="210" class="wp-image-865 aligncenter" /></a></li>
 	<li>Add a new User Claim by specifying 'Name' and 'Namespace' and select 'user.assignedroles' as source attribute. <span>Role mappings will be defined in Elasticsearch for this claim based on which Elasticsearch roles are assigned to each user. I have specified name as role and namespace as https://schemas.microsoft.com/ws/2008/06/identity/claims/role. Based on values you specify you will need to Elasticsearch.yml.</span><a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-1.59.44-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-1.59.44-PM-1024x402.png" alt="" width="640" height="251" class="wp-image-875 aligncenter" /></a></li>
 	<li>Application Roles: <span>The application roles are defined in </span><span>the application's registration manifest. When a user signs into the application, Azure AD emits a </span><code>roles</code><span> claim for each role that the user has been granted individually to the user and from their group membership. In order to add a role, you will need to edit the manifest for this App by navigating to <code>Azure Active Directory &gt; App Registrations &gt; Select Application and Edit Manifest</code>.</span> Please refer to this <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps" rel="noopener">document</a> about creating App Roles in Azure AD. Create a few roles by editing manifest as displayed below and save the manifest.<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-10.00.04-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-10.00.04-PM.png" alt="" width="506" height="546" class="wp-image-885 aligncenter" /></a></li>
 	<li>Assign Role to users: In order to assign roles to users, add a few users. N<span>avigate to </span><code>Azure Active Directory &gt; Enterprise Applications &gt; Select application &gt; Users and Groups &gt; Add User</code> and assign roles you created in previous step to these users. These roles are going to be mapped to Elasticsearch roles using role_mapping API in subsequent sections.</li>
 	<li>Finally, download Federation Metadata Xml and take note of <span>Azure AD Identifier URL as these are going to be needed later for setting up SAML based SSO in Elasticsearch. Since I have specified Elasticsearch as Application name, the name of this file is Elasticsearch.xml.</span></li>
</ul>
<h2>Encrypting communications in ELK Cluster</h2>
<span>X-Pack security enables you to encrypt traffic to, from, and within your Elasticsearch cluster. Connections are secured using Transport Layer Security (TLS), which is commonly referred to as "SSL". TLS requires X.509 certificates to perform encryption and authentication of the application that is being communicated with. In order for the communication between nodes to be truly secure, the certificates must be validated. The recommended approach for validating certificate authenticity in a Elasticsearch cluster is to trust the certificate authority (CA) that signed the certificate. By doing this, as nodes are added to your cluster they just need to use a certificate signed by the same CA and the node is automatically allowed to join the cluster. Additionally, it is recommended that the certificates contain subject alternative names (SAN) that correspond to the node’s IP address and DNS name so that hostname verification can be performed.</span>

<span>However, I will be generating self signed certificates using <code>elasticsearch-certutil</code> command line tool. </span>You can read more about <a target="_blank" href="https://www.elastic.co/guide/en/elastic-stack-overview/6.4/ssl-tls.html" rel="noopener">setting up TLS on a Cluster</a>. I already have Elasticsearch cluster running which I had created during first part of this series and I am going to harness the same to create self signed certificates. If you have certificates, you need to skip steps listed below to generate certificates.
<ul>
 	<li>Generate new local CA by running command <code>bin/elasticsearch-certutil ca</code>. The output file name is <code>elastic-stack-ca.p12</code> and password is Password1$. Change the name and password based on your preferences.</li>
 	<li>Generate self signed certificate <code>bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12 --dns sample-elasticsearch</code>. Enter password for CA (elastic-stack-ca.p12). The output file name is <code>elastic-certificates.p12</code> and password is Password1$. Change the name and password based on your preferences. The --dns parameter value 'sample-elasticsearch' is the Elasticsearch host name and needed for host name verification(especially for Logstash pipeline since hostname verification can't be turned off when ssl is enabled).</li>
 	<li>Generate a cert to c<span>onfigure Kibana to encrypt communications between the browser and Kibana server </span><code>bin/elasticsearch-certutil cert --pem -ca elastic-stack-ca.p12 --dns KIBANA_URL</code>. Update the --dns parameter value based on your DNS for Kibana endpoint. This is the same value which was specified during SAML configuration. The output file name is <code>certificate-bundle.zip</code>. If you unzip this folder, you will see <code>instance.crt</code> and <code>instance.key</code> files which we are going to need for setting up TLS in Kibana.</li>
</ul>
I have generated certs on existing Elasticsearch container, thus the next step is to copy these certs from AKS container to local machine. I will use kubectl command line tool to copy these files from AKS container to certs folder on my machine as displayed below
<code>kubectl cp default/sample-elasticsearch-0:elastic-stack-ca.p12 /Users/username/certs/elastic-stack-ca.p12</code><code>
kubectl cp default/sample-elasticsearch-0:elastic-certificates.p12 /Users/username/certs/elastic-certificates.p12</code><code></code><code>
kubectl cp default/sample-elasticsearch-0:certificate-bundle.zip /Users/username/certs/certificate-bundle.zip
</code>

After copying these files to local machine, I used OpenSSL utility to create <code>elastic-stack-ca.pem</code> file which Kibana and Logstash will need to communicate with TLS enabled Elasticsearch <code>openssl pkcs12 -in /Users/username/certs/elastic-stack-ca.p12 -clcerts -nokeys -chain -out /Users/username/certs/elastic-stack-ca.pem</code>

Now that certificates needed to enable TLS are ready, the next step is to create Elasticsearch, Logstash and Kibana resources. I will share couple of ways in which certificates and IDP federation metadata xml files can be deployed to ELK containers. In the first option, I am going to create custom docker images for Elasticsearch, Logstash and Kibana by bundling files needed to enable TLS and setup SAML based SSO. In the second option, I will create Secrets in Kubernetes for these files and mount these files instead of custom docker images.

The second option is an afterthought after I posted this blog since I didn't like the idea of bundling certificates and files into docker image. The second option uses Secrets and Command concepts which are explained in subsequent sections. Thus instead of composing a new article, I have decided to update the existing article. The headers of steps which are specific to option A or option B have been updated.

The solution structure of source code which you can download from GitHub is displayed below and you can see that I have copied cert files created in steps above and Elasticsearch.xml (Federation Metadata Xml). You will also see Dockerfiles per folder which I will need to create custom images (needed for first option only as mentioned above). I haven't committed certificates and IDP federation metadata xml thus you need to copy files you have created to corresponding folders before building docker images.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-11.03.44-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-08-at-11.03.44-PM.png" alt="" width="433" height="657" class="alignnone wp-image-895" /></a>

The next step is to create Elasticsearch, Logstash and Kibana resources in AKS. <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/24/azure-kubernetes-service-aks-deploying-elasticsearch-logstash-and-kibana-elk-and-consume-messages-from-azure-event-hub/" rel="noopener">Part 1</a> of this series provides detailed overview of these resources thus in this article I will only cover differences and changes needed to enable SSO and TLS as compared to Part 1 files.
<h2>Elasticsearch</h2>
Copy <code>elastic-stack-ca.p12, elastic-certificates.p12 and Elasticsearch.xml</code> to Elasticsearch folder. No changes are needed in ES_PersistentVolumeClaim.yml and ES_Service.yml files. The complete yaml files needed to enable TLS and deploy Elasticsearch to AKS can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO/Elasticsearch" rel="noopener">GitHub</a>.
<h3>Elasticsearch Dockerfile (this step is specific to first option)</h3>
Elasticsearch DockerFile copies certs and federation metadata xml to config folder of Elasticsearch and sets keystore and truststore passwords. You need to update file names and passwords based on your specified values in previous steps.

<code><span> <span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">docker.elastic.co/elasticsearch/elasticsearch:6.4.1</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">--chown=elasticsearch:elasticsearch elastic-stack-ca.p12 /usr/share/elasticsearch/config/</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">--chown=elasticsearch:elasticsearch elastic-certificates.p12 /usr/share/elasticsearch/config/</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">--chown=elasticsearch:elasticsearch Elasticsearch.xml /usr/share/elasticsearch/config/</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">/bin/bash -c </span><span style="color: #db7100">"</span><span style="color: #db7100">echo Password1$ | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password  -xf</span><span style="color: #db7100">"</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">/bin/bash -c </span><span style="color: #db7100">"</span><span style="color: #db7100">echo Password1$ | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password  -xf</span><span style="color: #db7100">"</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">/bin/bash -c </span><span style="color: #db7100">"</span><span style="color: #db7100">echo Password1$ | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password  -xf</span><span style="color: #db7100">"</span>
<span style="color: #009695">RUN</span><span style="color: #222222"> </span><span style="color: #222222">/bin/bash -c </span><span style="color: #db7100">"</span><span style="color: #db7100">echo Password1$ | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.http.ssl.truststore.secure_password  -xf;</span><span style="color: #db7100">"</span></span>
</code>
The commands to build and publish docker images to Docker Hub are
<ul>
 	<li>Build Docker Image: docker build -t testelasticsearch .</li>
 	<li>Tag Docker Image: docker tag testelasticsearch {YOUR_DOCKER_REPO}</li>
 	<li>Publish Image to Docker Hub: docker push {YOUR_DOCKER_REPO}</li>
</ul>
Please update {YOUR_DOCKER_REPO} placeholder with your docker repository.
<h3>Create A Kubernetes Secret (this step is specific to second option)</h3>
Since in second option, no custom docker files are needed, I am going to create secret for certificates and federation xml files. Kubernetes will save secret in base64 format. The command to create a secret from multiple files is

<code>kubectl create secret generic elasticsearchsecret --from-file=./Elasticsearch/elastic-certificates.p12 --from-file=./Elasticsearch/Elasticsearch.xml --from-file=./Elasticsearch/elastic-stack-ca.p12</code>
<h3>Create a Kubernetes ConfigMap</h3>
The main pointers about changes needed to this resource as compared to first part of this series are
<ul>
 	<li>SSL is enabled for Elasticsearch: <code>xpack.security.transport.ssl.enabled: true</code>, <code>xpack.security.http.ssl.enabled: true</code></li>
 	<li>Verification of certificate is set <code>xpack.security.transport.ssl.verification_mode: certificate</code></li>
 	<li>Keystore and Truststore type is<code>PKCS12</code>, path is location of cert bundled in image <code>elastic-certificates.p12</code> and password is Password1$. Update these values based on your specified values.</li>
 	<li>Cross origin resource sharing is enabled <code>http.cors.enabled: true</code></li>
 	<li>Two realms are specified i.e. native and saml under<code>xpack: security: authc: realms:</code>. Order of native realm is 0 so it will take precedence over saml realm.</li>
 	<li><code>idp.metadata.path</code> is path of the Federation metadata xml which was downloaded from Azure AD SAML configuration.</li>
 	<li><code>idp.entity_id</code> is the Azure AD identifier URL which was copied from Azure AD SAML configuration.</li>
 	<li><code>sp.entity_id</code> is the Kibana endpoint</li>
 	<li><code>sp.acs</code> is the Kibana reply URL</li>
 	<li><code>sp.logout</code> is the Kibana logout URL</li>
 	<li><code>attributes.principal</code> is mapped to namespace of the name user claim configured in Azure AD</li>
 	<li><code>attributes.groups</code> is mapped to namespace of the role user claim configured in Azure AD</li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">elasticsearch.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">cluster.name</span><span style="color: #222222">:</span><span style="color: #db7100"> "sample-elasticsearch-cluster"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">network.host</span><span style="color: #222222">:</span><span style="color: #db7100"> 0.0.0.0</span>
<span style="color: #222222">    </span><span style="color: #3363a4">discovery.zen.minimum_master_nodes</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">   </span><span style="color: #888a85"> #</span><span style="color: #888a85"> Update max_local_storage_nodes value based on number of nodes</span>
<span style="color: #222222">    </span><span style="color: #3363a4">node.max_local_storage_nodes</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.collection.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.transport.ssl.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.transport.ssl.verification_mode</span><span style="color: #222222">:</span><span style="color: #db7100"> certificate</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.license.self_generated.type</span><span style="color: #222222">:</span><span style="color: #db7100"> trial</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.keystore.type</span><span style="color: #222222">:</span><span style="color: #db7100"> PKCS12</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.keystore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.keystore.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.truststore.type</span><span style="color: #222222">:</span><span style="color: #db7100"> PKCS12</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.truststore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.truststore.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.transport.ssl.keystore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.transport.ssl.truststore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.http.ssl.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.http.ssl.keystore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.http.ssl.truststore.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "elastic-certificates.p12"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.ssl.verification_mode</span><span style="color: #222222">:</span><span style="color: #db7100"> certificate</span>
<span style="color: #222222">    </span><span style="color: #3363a4">http.cors.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">http.cors.allow-origin</span><span style="color: #222222">:</span><span style="color: #db7100"> "*"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">http.max_header_size</span><span style="color: #222222">:</span><span style="color: #db7100"> 16kb</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">security</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">authc</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">realms</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">native1</span><span style="color: #222222">:</span>
<span style="color: #222222">                </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> native</span>
<span style="color: #222222">                </span><span style="color: #3363a4">order</span><span style="color: #222222">:</span><span style="color: #db7100"> 0</span>
<span style="color: #222222">            </span><span style="color: #3363a4">saml1</span><span style="color: #222222">:</span>
<span style="color: #222222">              </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> saml</span>
<span style="color: #222222">              </span><span style="color: #3363a4">order</span><span style="color: #222222">:</span><span style="color: #db7100"> 2</span>
<span style="color: #222222">              </span><span style="color: #3363a4">idp.metadata.path</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/elasticsearch/config/Elasticsearch.xml"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">idp.entity_id</span><span style="color: #222222">:</span><span style="color: #db7100"> "AZURE_AD_IDENTIFIER_URL"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">sp.entity_id</span><span style="color: #222222">:</span><span style="color: #db7100">  "https://KIBANA_URL:5601/"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">sp.acs</span><span style="color: #222222">:</span><span style="color: #db7100"> "https://KIBANA_URL:5601/api/security/v1/saml"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">sp.logout</span><span style="color: #222222">:</span><span style="color: #db7100"> "https://KIBANA_URL:5601/logout"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">attributes.principal</span><span style="color: #222222">:</span><span style="color: #db7100"> "https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"</span>
<span style="color: #222222">              </span><span style="color: #3363a4">attributes.groups</span><span style="color: #222222">:</span><span style="color: #db7100"> "https://schemas.microsoft.com/ws/2008/06/identity/claims/role"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">role_mapping.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
</span></code>
<h3>Create a Kubernetes StatefulSet (this step is specific to first option)</h3>
Update ES_Statefulset.yaml file to point to Docker image <code>image: YOUR_ELASTICSEARCH_IMAGE</code>
<h3>Create a Kubernetes StatefulSet (this step is specific to second option)</h3>
I have created ES_Statefulset_V2.yaml file and main pointers are
<ul>
 	<li><code>securityContext: runAsUser: 2000</code> will run Elasticsearch as a non-root user.</li>
 	<li><code>command:["/bin/bash","-c", ...]</code> is going to execute several commands piped together when POD is started
<ul>
 	<li>is going to assign keystore and truststore passwords</li>
 	<li>set bootstrap.password</li>
 	<li>start ElasticSearch</li>
 	<li>you can also use shell script</li>
</ul>
</li>
 	<li>Environment variable <code>- name: PASSWORD</code> is created which is used in commands. I have specified value in yaml however a better option is to mount this from secret e.g <code>env[].valueFrom.secretKeyRef</code></li>
 	<li>Added <code>- name: sample-elasticsearch-elasticsearchsecret</code> volume and mounting <code>elastic-stack-ca.p12</code>, <code>elastic-certificates.p12</code> and <code>Elasticsearch.xml</code> from secret.</li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> StatefulSet</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">serviceName</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222"> </span><span style="color: #888a85"> #</span><span style="color: #888a85"> Number of PODS</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">terminationGracePeriodSeconds</span><span style="color: #222222">:</span><span style="color: #db7100"> 300</span>
<span style="color: #222222">      </span><span style="color: #3363a4">initContainers</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #888a85"> #</span><span style="color: #888a85"> https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> increase-the-vm-max-map-count</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> busybox</span>
<span style="color: #222222">        </span><span style="color: #3363a4">command</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">sysctl</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">-w</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">vm.max_map_count=262144</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> docker.elastic.co/elasticsearch/elasticsearch:6.4.1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">runAsUser</span><span style="color: #222222">:</span><span style="color: #db7100"> 2000</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 2000m</span>
<span style="color: #222222">            </span><span style="color: #3363a4">memory</span><span style="color: #222222">:</span><span style="color: #db7100"> 2Gi</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">            </span><span style="color: #3363a4">memory</span><span style="color: #222222">:</span><span style="color: #db7100"> 1Gi</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9200</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9300</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> tcp</span>
<span style="color: #222222">        </span><span style="color: #3363a4">command</span><span style="color: #222222">:</span><span style="color: #db7100"> ["/bin/bash","-c", "echo $PASSWORD | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password  -xf; \</span>
<span style="color: #222222">                echo $PASSWORD | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password  -xf; \</span>
<span style="color: #222222">                echo $PASSWORD | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password  -xf ; \</span>
<span style="color: #222222">                echo $PASSWORD | /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.http.ssl.truststore.secure_password  -xf ; \</span>
<span style="color: #222222">                echo $PASSWORD | /usr/share/elasticsearch/bin/elasticsearch-keystore add bootstrap.password  -xf; \</span>
<span style="color: #222222">                /usr/share/elasticsearch/bin/elasticsearch ;"]</span>
<span style="color: #222222">        </span><span style="color: #3363a4">env</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> cluster.name</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> "sample-elasticsearch-cluster"</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> "xpack.security.enabled"</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> "true"</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> "TAKE_FILE_OWNERSHIP"</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> “1”</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> PASSWORD</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/data</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/elasticsearch.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> elasticsearch.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/role_mapping.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> role_mapping.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-elasticsearchsecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/elastic-stack-ca.p12</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> elastic-stack-ca.p12</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-elasticsearchsecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/elastic-certificates.p12</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> elastic-certificates.p12</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-elasticsearchsecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/Elasticsearch.xml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> Elasticsearch.xml</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">        </span><span style="color: #3363a4">persistentVolumeClaim</span><span style="color: #222222">:</span>
<span style="color: #222222">           </span><span style="color: #3363a4">claimName</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">        </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-elasticsearchsecret</span>
<span style="color: #222222">        </span><span style="color: #3363a4">secret</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">secretName</span><span style="color: #222222">:</span><span style="color: #db7100"> elasticsearchsecret</span></span></code>
<h2>Kibana</h2>
Copy <code>instance.crt,instance.key andelastic-stack-ca.pem</code> to Kibana folder. The complete yaml files needed to deploy Kibana to AKS can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO/Kibana" rel="noopener">GitHub</a>.
<h3>Kibana Dockerfile (this step is specific to first option)</h3>
Kibana Dockerfile copies instance.crt and instance.key needed to enable TLS for Kibana along with elastic-stack-ca.pem so that Elasticsearch can trust Kibana.

<code><span><span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">docker.elastic.co/kibana/kibana:6.4.1</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">instance.crt /usr/share/kibana/config/</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">instance.key /usr/share/kibana/config/</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">elastic-stack-ca.pem /usr/share/kibana/config/</span></span></code>
<h3>Create A Kubernetes Secret (this step is specific to second option)</h3>
Since in second option no custom docker files are needed, I am going to create secret for certificates needed for Kibana. I will update the Deployment.yaml to mount these files from secret. The command to create a secret from multiple files is

<code>kubectl create secret generic kibanasecret --from-file=./Kibana/elastic-stack-ca.pem --from-file=./Kibana/instance.crt --from-file=./Kibana/instance.key</code>
<h3>Create a Kubernetes ConfigMap</h3>
The main pointers about changes needed to this resource as compared to first part of this series are
<ul>
 	<li>Elasticsearch is TLS enabled <code>elasticsearch.url: https://sample-elasticsearch:9200</code></li>
 	<li><code>elasticsearch.ssl.certificateAuthorities</code> is set to path of <code>elastic-stack-ca.pem</code></li>
 	<li>SSL is enabled <code>server.ssl.enabled: true</code></li>
 	<li><code>server.ssl.key</code> is set to path of  <code>instance.key</code></li>
 	<li><code>server.ssl.certificate</code> is set to path of  <code>instance.crt</code></li>
 	<li><code>xpack.monitoring.elasticsearch.ssl.verificationMode</code> is set to <code>certificate</code></li>
 	<li><code>elasticsearch.ssl.verificationMode</code> is set to <code>certificate</code></li>
 	<li><code>xpack.security.authProviders</code> is set to <code>[saml, basic]</code>. The order is important here as Kibana will use saml realm unless basic authorization header is passed in the request and in that case it will give precedence to native realm.</li>
 	<li><code>xpack.security.public</code> is specified so that if<span> Kibana instance is behind a proxy, it will tell Kibana how to form its public URL</span></li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">kibana.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.host</span><span style="color: #222222">:</span><span style="color: #db7100"> "0.0.0.0"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.url</span><span style="color: #222222">:</span><span style="color: #db7100"> https://sample-elasticsearch:9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.ui.container.elasticsearch.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.username</span><span style="color: #222222">:</span><span style="color: #db7100"> kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.encryptionKey</span><span style="color: #222222">:</span><span style="color: #db7100"> "something_at_least_32_characters"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.ssl.certificateAuthorities</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/kibana/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.ssl.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.ssl.key</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/kibana/config/instance.key"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.ssl.certificate</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/kibana/config/instance.crt"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.ssl.verificationMode</span><span style="color: #222222">:</span><span style="color: #db7100"> certificate</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.ssl.verificationMode</span><span style="color: #222222">:</span><span style="color: #db7100"> certificate</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.authProviders</span><span style="color: #222222">:</span><span style="color: #db7100"> [saml, basic]</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.xsrf.whitelist</span><span style="color: #222222">:</span><span style="color: #db7100"> [/api/security/v1/saml]</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.public</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">protocol</span><span style="color: #222222">:</span><span style="color: #db7100"> https</span>
<span style="color: #222222">      </span><span style="color: #3363a4">hostname</span><span style="color: #222222">:</span><span style="color: #db7100"> KIBANA_URL</span></span></code>
<h3>Create a Kubernetes Deployment (this step is specific to first option)</h3>
Update Kibana_Deployment.yaml file to point to Docker image <code>image: YOUR_KIBANA_IMAGE</code>
<h3>Create a Kubernetes Deployment (this step is specific to second option)</h3>
I have created Kibana_Deployment_V2.yaml file and main pointers are
<ul>
 	<li>Added <code>- name: sample-kibana-kibanasecret</code> volume and mounting <code>instance.crt</code>, <code>instance.key</code> and <code>elastic-stack-ca.pem</code> files from secret.</li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1beta1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Deployment</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> docker.elastic.co/kibana/kibana:6.4.1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 1000m</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 5601</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/kibana/config/kibana.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> kibana.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-kibanasecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/kibana/config/elastic-stack-ca.pem</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> elastic-stack-ca.pem</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-kibanasecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/kibana/config/instance.crt</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> instance.crt</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-kibanasecret</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/kibana/config/instance.key</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> instance.key</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span><span style="color: #db7100"> {}</span>
<span style="color: #222222">        </span><span style="color: #3363a4">terminationMessagePath</span><span style="color: #222222">:</span><span style="color: #db7100"> "/dev/termination-log"</span>
<span style="color: #222222">        </span><span style="color: #3363a4">terminationMessagePolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> File</span>
<span style="color: #222222">        </span><span style="color: #3363a4">imagePullPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> Always</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> false</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">        </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-kibanasecret</span>
<span style="color: #222222">        </span><span style="color: #3363a4">secret</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">secretName</span><span style="color: #222222">:</span><span style="color: #db7100"> kibanasecret</span>
<span style="color: #222222">      </span><span style="color: #3363a4">restartPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> Always</span>
<span style="color: #222222">      </span><span style="color: #3363a4">terminationGracePeriodSeconds</span><span style="color: #222222">:</span><span style="color: #db7100"> 5</span>
<span style="color: #222222">      </span><span style="color: #3363a4">dnsPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> ClusterFirst</span>
<span style="color: #222222">      </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">      </span><span style="color: #3363a4">schedulerName</span><span style="color: #222222">:</span><span style="color: #db7100"> default-scheduler</span></span></code>
<h3>Create a Kubernetes Service</h3>
Update Kibana_Service.yml file to use 5601 port since we have specified 5601 port for Kibana during SAML configuration and Elasticsearch.yml.

<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Service</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> LoadBalancer</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 5601</span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span></span></code>
<h2>Logstash</h2>
Copy <code>elastic-stack-ca.pem</code> to Elasticsearch folder. The yaml files needed to deploy Logstash to AKS can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO/Logstash" rel="noopener">GitHub</a>.
<h3>Logstash Dockerfile (this step is specific to option)</h3>
Logstash DockerFile copies certs needed to enable Logstash to communicate with Elasticsearch.

<code><span><span style="color: #009695">FROM</span><span style="color: #222222"> </span><span style="color: #222222">docker.elastic.co/logstash/logstash:6.4.1</span>
<span style="color: #009695">COPY</span><span style="color: #222222"> </span><span style="color: #222222">elastic-stack-ca.pem /usr/share/logstash/config/</span></span></code>
<h3>Create A Kubernetes Secret (this step is specific to second option)</h3>
Since in second option, no custom docker files are needed, I am going to create secret for certificates needed for Logstash. The command to create a secret from file is

<code>kubectl create secret generic logstashsecret --from-file=./Logstash/elastic-stack-ca.pem</code>
<h3>Create a Logstash ConfigMap</h3>
The main pointers about changes needed to this resource as compared to first part of this series are
<ul>
 	<li>Elasticsearch is TLS enabled <code>xpack.monitoring.elasticsearch.url: https://sample-elasticsearch:9200</code></li>
 	<li><code>xpack.monitoring.elasticsearch.ssl.ca</code> is set to point to <code>elastic-stack-ca.pem</code></li>
 	<li>Pipeline config for <code>azureeventhubs.cfg</code> is updated to
<ul>
 	<li>Enable ssl <code>ssl =&gt; true</code></li>
 	<li>Set <code>cacert</code> to point to <code>elastic-stack-ca.pem</code></li>
</ul>
</li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.url</span><span style="color: #222222">:</span><span style="color: #db7100"> https://sample-elasticsearch:9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">dead_letter_queue.enable</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.username</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash_system </span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.ssl.ca</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">pipelines.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">pipeline.id</span><span style="color: #222222">:</span><span style="color: #db7100"> azureeventhubs</span>
<span style="color: #222222">      </span><span style="color: #3363a4">path.config</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/azureeventhubs.cfg"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">azureeventhubs.cfg</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    input {</span>
<span style="color: #222222">      azure_event_hubs {</span>
<span style="color: #222222">        event_hub_connections =&gt; ["{AZURE_EVENT_HUB_CONNECTION_STRING};EntityPath=logstash"]</span>
<span style="color: #222222">        threads =&gt; 2</span>
<span style="color: #222222">        decorate_events =&gt; true</span>
<span style="color: #222222">        consumer_group =&gt; "$Default"</span>
<span style="color: #222222">        storage_connection =&gt; "{STORAGE_ACCOUNT_CONNECTION_STRING}"</span>
<span style="color: #222222">        storage_container =&gt; "logstash"</span>
<span style="color: #222222">        }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    filter {</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    output {</span>
<span style="color: #222222">      elasticsearch {</span>
<span style="color: #222222">        hosts =&gt; ["sample-elasticsearch:9200" ]</span>
<span style="color: #222222">        user =&gt; "elastic"</span>
<span style="color: #222222">        password =&gt; "Password1$"</span>
<span style="color: #222222">        index =&gt; "azureeventhub-%{+YYYY.MM.dd}"</span>
<span style="color: #222222">        ssl =&gt; true</span>
<span style="color: #222222">        cacert =&gt; "/usr/share/logstash/config/elastic-stack-ca.pem"</span>
<span style="color: #222222">      }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.conf</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span></span></code>
<h3>Create a Logstash Deployment (this step is specific to first option)</h3>
Update Logstash_Deployment.yaml file to point to Docker image <code>image: YOUR_LOGSTASH_IMAGE</code>
<h3>Create a Logstash Deployment (this step is specific to second option)</h3>
I have created Logstash_Deployment_V2.yaml file and main pointers are
<ul>
 	<li>Added <code>- name: sample-logstash-logstashsecret</code> volume and mounting <code>elastic-stack-ca.pem</code> from secret.</li>
</ul>
<code><span><span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1beta1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Deployment</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> YOUR_LOGSTASH_IMAGE</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/config/logstash.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/pipeline/logstash.conf</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash.conf</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/azureeventhubs.cfg</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> azureeventhubs.cfg</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/azureredis.cfg</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> azureredis.cfg</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/config/pipelines.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> pipelines.yml</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 1000m</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 5601</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span></span></code>
<h2>Deploy resources to AKS</h2>
Deploy Elasticsearch, Logstash and Kibana resources to AKS. Once external endpoint is created for Kibana service which takes a while proceed to next step to update UP with DNS name of Kibana.
<h2><span>Update a public IP resource with a DNS name</span></h2>
The IP address of the Kibana Service external endpoint is updated with DNS name using commands displayed below. You need to update IP and DNSNAME before running these commands based on your values.

<code>bash-3.2$ IP="KIBANA_SERVICE_EXTERNAL_ENDPOINT_IP"
bash-3.2$ DNSNAME="KIBANA_URL"
bash-3.2$ PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv)
bash-3.2$ az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME</code>
<h2>Map Users to Elasticsearch Roles using role_mapping API</h2>
Elasticsearch's role_mapping API is used to map user roles to Elasticsearch roles. Mapping between groups and role user claim along with mapping between principal and name user claim was defined in Elasticsearch.yml

<code>attributes.principal: "https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"</code>

<code>attributes.groups:"https://schemas.microsoft.com/ws/2008/06/identity/claims/role"</code>

A few examples of using role_mapping API to map Roles are
<ul>
 	<li>This maps all the users of saml1 realm to kibana_user Elasticsearch role <code>PUT /_xpack/security/role_mapping/aad-saml-allusers
{
"roles": [ "kibana_user" ],
"enabled": true,
"rules": {
"field": { "realm.name": "saml1" }
}
}</code></li>
 	<li>This maps users of saml1 realm having groups field value as superuser to map to superuser Elasticsearch role<code>PUT /_xpack/security/role_mapping/aad-saml-superuser
{
"roles": [ "superuser" ],
"enabled": true,
"rules": {
"all": [
{ "field": { "realm.name": "saml1" } },
{ "field": { "groups": "superuser" } }
]
}
}</code></li>
 	<li>This maps users of saml1 realm having groups field value as kibanauser to map to kibana_user Elasticsearch role<code>PUT /_xpack/security/role_mapping/aad-saml-kibanauser
{
"roles": [ "kibana_user" ],
"enabled": true,
"rules": {
"all": [
{ "field": { "realm.name": "saml1" } },
{ "field": { "groups": "kibanauser" } }
]
}
}</code></li>
</ul>
&nbsp;

This completes all the changes needed to enabled SAML based SSO and securing communications in ELK. Browse to <code>https://KIBANA_URL:5601</code> and Elasticsearch will redirect to Azure AD and after successful login you will be redirected back to Kibana. Browsers will warn since self signed certs are used and after granting access, Kibana Dashboard will launch

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-09-at-12.14.18-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-09-at-12.14.18-AM-1024x651.png" alt="" width="553" height="351" class="alignnone wp-image-905" /></a>

Browse to <code>https://KIBANA_URL:5601/api/security/v1/me</code> after authentication it will display mapped Elasticsearch roles and saml claims of the user.

On sending messages to Azure Event Hub using the utility I had shared in first part, messages are going to get ingested into Elasticsearch through Logstash pipeline. The source code of this utility can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/ELKSamples/AzureEventHubSample" rel="noopener">GitHub</a>.

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-09-at-12.17.00-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/10/Screen-Shot-2018-10-09-at-12.17.00-AM-1024x520.png" alt="" width="778" height="395" class="alignnone wp-image-915" /></a>

&nbsp;

<span>You can download source code for this article from</span><span> </span><a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/Azure%20AD%20SAML%20SSO" rel="noopener">GitHub repository</a>

&nbsp;

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Azure Functions 2.0: Create, debug and deploy to Azure Kubernetes Service (AKS)</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-functions-2-0-create-debug-and-deploy-to-azure-kubernetes-service-aks</link>
      <pubDate>Wed, 26 Sep 2018 16:08:38 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=745</guid>
      <description><![CDATA[Azure Functions is a serverless compute service that enables you to run code on-demand without...]]></description>
      <content:encoded><![CDATA[<span>Azure Functions is a serverless</span><span> compute service that enables you to run code on-demand without having to explicitly provision or manage infrastructure. You can read about </span>Azure Functions 2.0 general availability @ <a target="_blank" href="https://azure.microsoft.com/en-us/blog/introducing-azure-functions-2-0/" rel="noopener">Introducing Azure Functions 2.0</a>. <span> Runtime 2.0 runs on .NET Core 2, which means it can run on all platforms supported by .NET Core, including macOS and Linux. This enables cross-platform development and hosting scenarios.</span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.17.21-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.17.21-AM.png" alt="" width="437" height="309" class="alignnone wp-image-755" /></a>

In this article I am going to
<ul>
 	<li>Create Azure Functions triggered by Azure Blob storage and Event hub in Visual Studio Code</li>
 	<li>Debug locally in Visual Studio Code</li>
 	<li>Deploy Azure Functions to Azure Kubernetes Service</li>
</ul>
<span>Dev tools used to develop these components are Visual Studio Code for macOS, Docker, AKS Dashboard and kubectl.</span>
<h2>Development environment</h2>
The steps needed to configure development environment (Azure Function 2.0 on <span>macOS</span>) are
<ul>
 	<li>Install Visual Studio Code, if it's not already installed</li>
 	<li>Install 'Azure Functions for Visual Studio Code' extension</li>
 	<li><span>Use Homebrew to install the Core Tools on macOS</span>
<ul>
 	<li>Install <a href="https://www.microsoft.com/net/download/macos">.NET Core 2.1 for macOS</a>, if it's not already installed</li>
 	<li>Install <a href="https://brew.sh/">Homebrew</a>, if it's not already installed.</li>
 	<li>Install the Core Tools package:
<ul>
 	<li>Run command <code>brew tap azure/functions</code></li>
</ul>
</li>
</ul>
</li>
 	<li style="list-style-type: none">
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>Before running the next command to install core tools, grant write access by running command
<code>sudo chown -R $(whoami) /usr/local/share/man/man5 /usr/local/share/man/man7</code></li>
 	<li>Run command <code>brew install azure-functions-core-tools</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>Azure Kubernetes Service (AKS) Cluster</h2>
In case you don’t have AKS cluster up and running, please go through this article to <a href="https://docs.microsoft.com/en-us/azure/aks/create-cluster"><span>Create AKS Cluster</span></a>.
<h2>Azure Blob Storage</h2>
<span><strong>AzureWebJobsStorage:</strong> The Azure Functions runtime uses storage account connection string for all functions except for HTTP triggered functions. The storage account must be a general-purpose one that supports blobs, queues, and tables. Create a storage account which is going to be used by Azure Functions runtime</span>. Please follow steps listed in this article to<span> </span><a target="_blank" href="https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account" rel="noopener">create a storage account</a>.
<div><strong>AzureBlobStorage: </strong>Instead of creating a blob container in previously created storage account, I created a separate storage account and created a blob container namely 'testblob'. If you specify a different container name, you will need to update blob container name in TestBlobStorageFunc class. Container name and storage connection string will be specified in Azure function triggered by blog storage as we are going to see shortly.</div>
Keep a note of Storage connection strings for both accounts as these need to be updated in 'local.settings.json' and 'DockerFile' files.
<h2>Event Hub</h2>
I created an Event hub and Event Hub name I have specified in sample solution is 'testeventhub'. If you select a different name, update event hub name in TestEventHubFunc class. Please follow steps listed in this article to<span> </span><a target="_blank" href="https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-create" rel="noopener">create an Event Hub</a>.

Keep a note of Event hub connection string as this needs to be updated in 'local.settings.json' and 'DockerFile' files.
<h2>Create Azure Functions 2.0</h2>
The next step is to create Azure Functions project in Visual Studio Code. Open the folder in integrated terminal of Visual Studio Code and
<ul>
 	<li>Run command <code>func init --docker</code> to create Function 2.0 project and docker file as displayed below<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.34.26-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.34.26-AM.png" alt="" width="287" height="207" class="alignnone wp-image-765" /></a></li>
 	<li>If you open host.json file you will see <code>"version": "2.0"</code> which is required for a function app that targets V2 runtime.</li>
 	<li>Run command <code>func new</code> to create a new function and select BlobTrigger option and provide function name<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.37.31-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.37.31-AM.png" alt="" width="363" height="216" class="alignnone wp-image-775" /></a></li>
 	<li>Run command <code>func new</code> to create a new function and select EventHubTrigger and provide function name<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.39.45-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.39.45-AM.png" alt="" width="367" height="215" class="alignnone wp-image-785" /></a></li>
</ul>
<h2>Debug Azure Functions 2.0 locally in Visual Studio Code:</h2>
Before debugging locally, 'local.settings.json' file needs to be updated with connection strings of Storage accounts and Event Hub.<code>
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "{BLOB_STORAGE_CONNECTION_STRING}",
"AzureBlobStorage": "{BLOB_STORAGE_CONNECTION_STRING_OF_CONTAINER_HAVING_TRIGGER}",
"AzureEventHubConnectionString":"{EVENT_HUB_CONNECTION_STRING}",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
</code>

Update BlobTrigger function and specify container name and connection e.g.
<code>public static void Run([BlobTrigger("testblob/{name}", Connection = "AzureBlobStorage")]Stream myBlob, string name, ILogger log)</code>

Update EventHubTrigger function and specify event hub name and connection e.g.
<code>public static void Run([EventHubTrigger("testeventhub", Connection = "AzureEventHubConnectionString")]string myEventHubMessage, ILogger log)</code>

Start debugging in Visual Studio Code and Functions will get triggered once you send a few messages to Event hub and upload a few blobs.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.44.52-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-5.44.52-AM-1024x185.png" alt="" width="371" height="67" class="alignnone wp-image-795" /></a>
<h2> Deploy Azure Functions 2.0 to AKS</h2>
The Values defined in 'local.settings.json' are used only for debugging locally. Thus, we need to specify Values before deploying to AKS. In order to do that DockerFile needs to be updated so that we can pass connection strings through environment variables. Add environment variables listed below to DockerFile and update placeholders with your connection strings.

<code>ENV AzureWebJobsStorage="{BLOB_STORAGE_CONNECTION_STRING}"
ENV AzureBlobStorage="{BLOB_STORAGE_CONNECTION_STRING_OF_CONTAINER_HAVING_TRIGGER}"
ENV AzureEventHubConnectionString="{EVENT_HUB_CONNECTION_STRING}"</code>

Finally, it's time to deploy functions to AKS and command is <code>func deploy --platform kubernetes --name {DockerHubRepositoryName} --registry {DockerHubRegistry}</code>.  Update placeholders with your Docker hub repository name and account.

Once deployment is complete, you can open the endpoint to verify the deployment. You can also open AKS dashboard to view deployed resources under namespace 'azure-functions'.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-6.12.46-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-6.12.46-AM-1024x545.png" alt="" width="680" height="362" class="alignnone wp-image-805" /></a>

You will find that Horizontal Pod Autoscaler resource is created which <span>automatically scales the number of pods in a replication controller, deployment or replica set based on observed CPU utilization.</span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-6.13.19-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-26-at-6.13.19-AM-1024x103.png" alt="" width="684" height="69" class="alignnone wp-image-815" /></a>

Send a few messages to Event Hub and upload a few blobs. You can view logs of functions deployed to AKS by running command <code>func logs --name {DockerHubRepositoryName} --platform kubernetes</code>. Update DockerHubRepositoryName placeholder with the name you specified in previous step.
<div></div>
<div>This completes the article about deploying Functions 2.0 to Kubernetes. You can download the source code for this article from <a target="_blank" href="https://github.com/atverma/azurekubernetesservice-azurefunctions-2.0" rel="noopener">GitHub</a>.</div>
<div></div>
&nbsp;
&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Azure Kubernetes Service (AKS): Deploying Elasticsearch, Logstash and Kibana (ELK) and consume messages from Azure Event Hub</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-kubernetes-service-aks-deploying-elasticsearch-logstash-and-kibana-elk-and-consume-messages-from-azure-event-hub</link>
      <pubDate>Mon, 24 Sep 2018 04:15:09 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=565</guid>
      <description><![CDATA[This article is part of the series of blogs on Azure Kubernetes Service (AKS). In this article I am...]]></description>
      <content:encoded><![CDATA[This article is part of the series of blogs on Azure Kubernetes Service (AKS). In this article I am going to share my experience of setting up Elasticsearch, Logstash and Kibana cluster in Azure Kubernetes Service (AKS) and consume messages from Event Hub. After the end of this article, we are going to have fully functional ELK stack with Azure Event Hub integration.

A sample client App (e.g. IOT device) will be publishing messages to Event Hub and these messages will be ingested into Elasticsearch using 'Azure Event Hub' plugin of Logstash. This article needs x-pack features of Elasticsearch thus I will show steps needed to activate trial license.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-11.01.54-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-11.01.54-PM-1024x383.png" alt="" width="824" height="308" class="alignnone wp-image-715" /></a>

The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/10/09/azure-kubernetes-service-aks-azure-ad-saml-based-single-sign-on-to-secure-elasticsearch-and-kibana-and-securing-communications-in-elk/" rel="noopener">second part of this series </a> goes through <span>steps needed to enable Azure AD SAML based single sign on to secure Elasticsearch and Kibana hosted in AKS. The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/10/14/ingest-azure-redis-cache-messages-into-elasticsearch-logstash-and-kibana-cluster-deployed-in-azure-kubernetes-service-aks/" rel="noopener">third part of this series</a> goes through steps needed to ingest Azure Redis Cache messages into Elasticsearch using Logstash's Redis plugin.</span>

<span>The dev tools used to develop these components are Visual Studio for Mac/Visual Studio 2017, AKS Dashboard as well as kubectl commands are used to create/manager Kubernetes resources in AKS.</span>
<h2>Azure Kubernetes Service (AKS) Cluster</h2>
In case you don’t have AKS cluster up and running, please go through this article to <a href="https://docs.microsoft.com/en-us/azure/aks/create-cluster"><span>Create AKS Cluster</span></a>. Once AKS cluster is up and running, you can go through the rest of the article. The code snippets in this article are mostly yaml snippets and are included for reference only as formatting may get distorted thus please refer to<span> </span><a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK" rel="noopener">GitHub repository</a><span> </span>for formatted resources.
<h2>Event Hub</h2>
Event hub messages will be ingested into Elasticsearch using Logstash pipeline, thus first resource I am going to create is Event Hub. Please follow steps listed in this article to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-create" rel="noopener">create an Event Hub</a>. The main pointers are
<ul>
 	<li>Event Hub name I have selected for this sample solution is 'logstash'. If you select a different name, update event hub name in the source code.</li>
 	<li>Keep a note of Event Hub connection string as this needs to be updated in Logstash pipeline and Event Hub messages publisher client.</li>
 	<li>Source code uses '$Default' Resource_Group. Update this value if you create a Resource_Group</li>
</ul>
<h2>Azure Blob Storage</h2>
The next resource which you will need to create is Azure Blob Storage. Please follow steps listed in this article to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account" rel="noopener">create a storage account</a>. Once storage account is created, create Blob Service-&gt; Blobs. The main pointers are
<ul>
 	<li>Blob container name I have specified in source code is 'logstash'. If you select a different name, update storage container name in the source code.</li>
 	<li>Keep a note of Storage connection string as this needs to be updated in Logstash pipeline.</li>
</ul>
<h2>Client App to send messages to Event Hub</h2>
AzureEventHubSample project is client app to send messages to Event hub. You will need to update connectionString variable with Event Hub connection string and name of the hub. You can download the source code of this publisher client from <a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK/tree/master/ELKSamples/AzureEventHubSample" rel="noopener">Github</a>.
<h2>Deploy Elasticsearch to Azure Kubernetes Service</h2>
Elasticsearch is a near real time search platform. The steps needed to deploy Elasticsearch to AKS cluster are listed below
<h3>Create a Persistent Volume</h3>
Persistent volume claim is needed to store Elasticsearch data. The yaml snippet to create a 5 GB storage is<span class="Apple-converted-space"> displayed below. The StatefulSet resource is going to mount files to this storage claim. You can read more about<span> </span><a target="_blank" href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" rel="noopener">Persistent Volumes.</a></span>

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> PersistentVolumeClaim</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">accessModes</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">ReadWriteOnce</span>
<span style="color: #222222">  </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">   </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">storage</span><span style="color: #222222">:</span><span style="color: #db7100"> 5Gi</span></span></code>
<h3>Create a Kubernetes ConfigMap</h3>
ConfigMaps allow you to decouple configuration aspects from image and a few pointers about the yaml snippet displayed below are
<ul>
 	<li>elasticsearch.yml and role_mapping.yml files will be mounted from ConfigMap</li>
 	<li>xpack-security is enabled</li>
 	<li>Update <code>max_local_storage_nodes</code> based on number of Pods</li>
 	<li>role_mapping.yml is intentionally left blank as it will be discussed in detail in next blogs of this series</li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">elasticsearch.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">cluster.name</span><span style="color: #222222">:</span><span style="color: #db7100"> "sample-elasticsearch-cluster"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">network.host</span><span style="color: #222222">:</span><span style="color: #db7100"> 0.0.0.0</span>
<span style="color: #222222">    </span><span style="color: #3363a4">discovery.zen.minimum_master_nodes</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">   </span><span style="color: #888a85"> #</span><span style="color: #888a85">Update max_local_storage_nodes value based on number of nodes</span>
<span style="color: #222222">    </span><span style="color: #3363a4">node.max_local_storage_nodes</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.security.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.collection.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">  </span><span style="color: #3363a4">role_mapping.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
</span></code>

You can read more about<span> </span><a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/" rel="noopener">ConfigMap</a>.
<h3>Create a Kubernetes Service</h3>
The next step is to create a Kubernetes Service for Elasticsearch. As you can see in yaml snippet below, port 9200/9300 is defined and type is ClusterIP i.e. this service doesn't has external endpoints. Kubernetes will use to selector '<code>service: sample-elasticsearch</code>' to map to the deployment as you are going to see next. You can read more about<span> </span><a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener">Services</a>.<span class="Apple-converted-space"> </span>

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1               </span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Service                </span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span><span style="color: #db7100">                    </span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch        </span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span><span style="color: #db7100">                    </span>
<span style="color: #222222">    </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span><span style="color: #db7100">                        </span>
<span style="color: #222222">  </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> ClusterIP</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span><span style="color: #db7100">                   </span>
<span style="color: #222222">    </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">  </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span><span style="color: #db7100">                     </span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http               </span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 9200               </span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">protocol</span><span style="color: #222222">:</span><span style="color: #db7100"> TCP            </span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> transport          </span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 9300               </span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9300</span>
<span style="color: #222222">    </span><span style="color: #3363a4">protocol</span><span style="color: #222222">:</span><span style="color: #db7100"> TCP</span></span></code>
<h3>Create a Kubernetes StatefulSet</h3>
Kubernetes StatefulSet is the workload API used to manage stateful applications. You can read more about <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/" rel="noopener">StatefulSets</a>. The yaml snippet to create a StatefulSet is displayed below and a few pointers are
<ul>
 	<li><code>service: sample-elasticsearch</code> matches to the selector defined in the service.</li>
 	<li>I have specified <code>replicas: 1</code> which means that only one instance of Pod will be created by Kubernetes. You can update this value as needed, however you will need to update ConfigMap value <code>.max_local_storage_nodes: 1</code> too.</li>
 	<li>The docker image being used to create this resource is <code>image:docker.elastic.co/elasticsearch/elasticsearch:6.4.1</code>.</li>
 	<li>By default, Elasticsearch runs inside the container as user elasticsearch using uid:gid 1000:1000. If you are bind-mounting a local directory or file, ensure it is readable by this user, while the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.4/path-settings.html" class="link" title="path.data and path.logs">data and log dirs</a><span> additionally require write access. This is the reason I have used environment variable  </span><code>- name: "TAKE_FILE_OWNERSHIP" value: “1”</code>. The alternate option is to grant write access by adding an initcontainer step e.g. <code>command:
- sh
- -c
- chown -R 1000:1000 /usr/share/elasticsearch/data</code>. You can read more at <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/6.4/docker.html" rel="noopener">Elasticsearch Docker</a>.</li>
 	<li>mmap count has been increased  <code>vm.max_map_count=262144</code> by adding an initcontainer step. You can read more about <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html" rel="noopener">Elasticsearch virtual memory</a>.</li>
 	<li>ConfigMap defined in previous step is used to mount elasticsearch.yml and role_mapping.yml files i.e. <code>configMap:
name: sample-elasticsearch-configmap</code>.</li>
 	<li>Lastly, persistent volume claim created above is used for storage i.e. <code>persistentVolumeClaim: claimName:sample-elasticsearch-data-claim</code>.</li>
 	<li>Assign CPU resource as needed by updating section <code>resources:limits:</code>. You can read more about <a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/" rel="noopener">Assign CPU Resources to Containers and Pods</a></li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> StatefulSet</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">serviceName</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222"> </span><span style="color: #888a85"> #</span><span style="color: #888a85"> Number of PODS</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">service</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">terminationGracePeriodSeconds</span><span style="color: #222222">:</span><span style="color: #db7100"> 15</span>
<span style="color: #222222">      </span><span style="color: #3363a4">initContainers</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #888a85"> #</span><span style="color: #888a85"> https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> increase-the-vm-max-map-count</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> busybox</span>
<span style="color: #222222">        </span><span style="color: #3363a4">command</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">sysctl</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">-w</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">vm.max_map_count=262144</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> docker.elastic.co/elasticsearch/elasticsearch:6.4.1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 2000m</span>
<span style="color: #222222">            </span><span style="color: #3363a4">memory</span><span style="color: #222222">:</span><span style="color: #db7100"> 2Gi</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">            </span><span style="color: #3363a4">memory</span><span style="color: #222222">:</span><span style="color: #db7100"> 1Gi</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9200</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 9300</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> tcp</span>
<span style="color: #222222">        </span><span style="color: #3363a4">env</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> cluster.name</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> "sample-elasticsearch-cluster"</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> "xpack.security.enabled"</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> "true"</span>
<span style="color: #222222">          </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> "TAKE_FILE_OWNERSHIP"</span>
<span style="color: #222222">            </span><span style="color: #3363a4">value</span><span style="color: #222222">:</span><span style="color: #db7100"> “1”</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/data</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/elasticsearch.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> elasticsearch.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/elasticsearch/config/role_mapping.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> role_mapping.yml</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">        </span><span style="color: #3363a4">persistentVolumeClaim</span><span style="color: #222222">:</span>
<span style="color: #222222">           </span><span style="color: #3363a4">claimName</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-data-claim</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span>
<span style="color: #222222">        </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-elasticsearch-configmap</span></span></code>

By default Elasticsearch will be deployed with basic license. After Elasticsearch is deployed, the next step is to activate trail license of Elasticsearch to use x-pack features of Elasticsearch.
<h3>Enable Trial Version of Elasticsearch</h3>
The steps needed to activate trail license are
<ul>
 	<li>Run command <code>kubectl port-forward sample-elasticsearch-0 9200:9200</code> and now you can access Elasticsearch endpoint at https://localhost:9200. POST <code>https://localhost:9200/_xpack/license/start_trial?acknowledge=true</code> request from any rest client. This is going to activate the trial license.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-2.58.50-PM1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-2.58.50-PM1-1024x600.png" alt="" width="627" height="367" class="wp-image-725 aligncenter" /></a>
<ul>
 	<li>You can verify if trail version is activated by GET <code>https://localhost:9200/_xpack/license</code> request from any rest client.</li>
 	<li>Once trial license is activated you can close the terminal as port forwarding isn't needed anymore.</li>
</ul>
<h3>Setup Password for buit-in user accounts of Elasticsearch</h3>
x-pack security feature of Elasticsearch is used to secure access thus we now need to setup passwords for built-in user accounts and the steps are
<ul>
 	<li>Connect to Elasticsearch POD by running command <code>kubectl exec -ti sample-elasticsearch-0  bash</code></li>
 	<li>Run command <code>bin/elasticsearch-setup-passwords interactive</code> to setup built-in user passwords interactively. For this sample I have specified same password i.e. Password1$ for all accounts thus you will need to change password you specified in a few places in source code.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-12.14.12-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-12.14.12-AM-1024x147.png" alt="" width="769" height="110" class=" wp-image-615 aligncenter" /></a>
<h2>Deploy Kibana to Azure Kubernetes Service</h2>
Kibana lets you visualize your Elasticsearch data and navigate the Elastic Stack. The steps needed to deploy Kibana to AKS are listed below
<h3>Create a Kubernetes ConfigMap</h3>
The yaml snippet to create this resource is displayed below and a few pointers are
<ul>
 	<li><code>kibana.yml</code> file will be mounted from ConfigMap</li>
 	<li>Kibana points to Elasticsearch based on <code>elasticsearch.url: https://sample-elasticsearch:9200</code></li>
 	<li>Update<code>elasticsearch.password: Password1$</code> based on your specified password for kibana (built-in) user</li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">kibana.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">server.host</span><span style="color: #222222">:</span><span style="color: #db7100"> "0"</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.url</span><span style="color: #222222">:</span><span style="color: #db7100"> https://sample-elasticsearch:9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.ui.container.elasticsearch.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.username</span><span style="color: #222222">:</span><span style="color: #db7100"> kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">elasticsearch.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span></span></code>
<h3>Create a Kubernetes Service</h3>
The next step is to create a Kubernetes Service for Kibana. As you can see in yaml snippet below, port 80 is defined and type is LoadBalancer i.e. this service has external endpoints. Kubernetes will use to selector '<code>component: sample-kibana</code>' to map to the deployment as you are going to see next. <span>The creation of this service is going to take a while and once done you can get the external endpoint of this service either by opening AKS Dashboard or running Kubectl command </span><code>kubectl describe services sample-kibana</code>.

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Service</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> LoadBalancer</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 80</span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span></span></code>
<h3>Create a Kubernetes Deployment</h3>
The next step is to create a Kubernetes Deployment for Kibana. The yaml snippet is displayed below and a few pointers are
<ul>
 	<li><span>The docker image being used to create this resource is <code>image: docker.elastic.co/kibana/kibana:6.4.1</code></span></li>
 	<li>You can change the number of pods by updating <code>replicas: 1</code></li>
 	<li>Label<code>component: sample-kibana</code> has to match the selector defined in the service</li>
 	<li>ConfigMap defined in previous step is used to mount kibana.yml file i.e. <code>configMap:
name: sample-kibana-configmap</code></li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1beta1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Deployment</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> docker.elastic.co/kibana/kibana:6.4.1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 1000m</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 5601</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/kibana/config/kibana.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> kibana.yml</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span><span style="color: #db7100"> {}</span>
<span style="color: #222222">        </span><span style="color: #3363a4">terminationMessagePath</span><span style="color: #222222">:</span><span style="color: #db7100"> "/dev/termination-log"</span>
<span style="color: #222222">        </span><span style="color: #3363a4">terminationMessagePolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> File</span>
<span style="color: #222222">        </span><span style="color: #3363a4">imagePullPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> Always</span>
<span style="color: #222222">        </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">privileged</span><span style="color: #222222">:</span><span style="color: #db7100"> false</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">        </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-kibana-configmap</span>
<span style="color: #222222">      </span><span style="color: #3363a4">restartPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> Always</span>
<span style="color: #222222">      </span><span style="color: #3363a4">terminationGracePeriodSeconds</span><span style="color: #222222">:</span><span style="color: #db7100"> 5</span>
<span style="color: #222222">      </span><span style="color: #3363a4">dnsPolicy</span><span style="color: #222222">:</span><span style="color: #db7100"> ClusterFirst</span>
<span style="color: #222222">      </span><span style="color: #3363a4">securityContext</span><span style="color: #222222">:</span><span style="color: #db7100"> {}</span>
<span style="color: #222222">      </span><span style="color: #3363a4">schedulerName</span><span style="color: #222222">:</span><span style="color: #db7100"> default-scheduler</span></span></code>

Open the external endpoint of Kibana service once deployment is completed. Since x-pack security is enabled, Kibana will prompt for credentials.
<h2>Deploy Logstash to Azure Kubernetes Service</h2>
<span>Logstash is data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to Elasticsearch. </span>Logstash will use Azure Event Hub plugin to ingest data into Elasticsearch. The steps needed to deploy Logstash to AKS are listed below
<h3>Create a Kubernetes ConfigMap</h3>
The yaml snippet to create ConfigMap is displayed below and a few pointers are
<ul>
 	<li><code>logstash.yml</code> file will be mounted from ConfigMap</li>
 	<li><code>pipelines.yml</code> file will be mounted from ConfigMap. Multiple pipelines can be defined in this file e.g. you can see that AzureEventHubs pipeline is defined.</li>
 	<li><code>azureeventhub.cfg</code> file will be mounted from ConfigMap. The Logstash event processing pipeline has three stages: inputs → filters → outputs. This file defines the logstash pipeline for Azure Event Hub.
<ul>
 	<li>Update <code>{AZURE_EVENT_HUB_CONNECTION_STRING}</code> and {<code>STORAGE_ACCOUNT_CONNECTION_STRING}</code> values based on your Event Hub and Storage Account values.</li>
 	<li>Update <code>EntityPath</code> in case your event hub is named differently then logstash.</li>
 	<li>Update <code>storage_container</code> in case your storage container is named differently than logstash.</li>
 	<li>Update <code>consumer_group</code> in case your consumer group is different than $Default. Specifying $Default isn't an ideal option.</li>
 	<li>Index name is defined as <code>index =&gt; "azureeventhub-%{+YYYY.MM.dd}</code></li>
</ul>
</li>
 	<li><code>logstash.conf</code> file will be mounted from ConfigMap</li>
 	<li>Elasticsearch URL is defined in <code>xpack.monitoring.elasticsearch.url: https://sample-elasticsearch:9200</code></li>
 	<li>Update<code>xpack.monitoring.elasticsearch.password: Password1$</code> based on your specified password for logstash_system (built-in) user.</li>
 	<li>Elasticsearch endpoint is defined in <code>hosts =&gt; [ "sample-elasticsearch:9200" ]</code></li>
 	<li>Update <code>password =&gt; "Password1$"</code> to the specified password of your elastic (built-in) user</li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> ConfigMap</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">  </span><span style="color: #3363a4">namespace</span><span style="color: #222222">:</span><span style="color: #db7100"> default</span>
<span style="color: #3363a4">data</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.url</span><span style="color: #222222">:</span><span style="color: #db7100"> https://sample-elasticsearch:9200</span>
<span style="color: #222222">    </span><span style="color: #3363a4">dead_letter_queue.enable</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.enabled</span><span style="color: #222222">:</span><span style="color: #db7100"> true</span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.username</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash_system </span>
<span style="color: #222222">    </span><span style="color: #3363a4">xpack.monitoring.elasticsearch.password</span><span style="color: #222222">:</span><span style="color: #db7100"> Password1$</span>
<span style="color: #222222">  </span><span style="color: #3363a4">pipelines.yml</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">pipeline.id</span><span style="color: #222222">:</span><span style="color: #db7100"> azureeventhubs</span>
<span style="color: #222222">      </span><span style="color: #3363a4">path.config</span><span style="color: #222222">:</span><span style="color: #db7100"> "/usr/share/logstash/azureeventhubs.cfg"</span>
<span style="color: #222222">  </span><span style="color: #3363a4">azureeventhubs.cfg</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span>
<span style="color: #222222">    input {</span>
<span style="color: #222222">      azure_event_hubs {</span>
<span style="color: #222222">        event_hub_connections =&gt; ["{AZURE_EVENT_HUB_CONNECTION_STRING};EntityPath=logstash"]</span>
<span style="color: #222222">        threads =&gt; 2</span>
<span style="color: #222222">        decorate_events =&gt; true</span>
<span style="color: #222222">        consumer_group =&gt; "$Default"</span>
<span style="color: #222222">        storage_connection =&gt; "{STORAGE_ACCOUNT_CONNECTION_STRING}"</span>
<span style="color: #222222">        storage_container =&gt; "logstash"</span>
<span style="color: #222222">        }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    filter {</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">    output {</span>
<span style="color: #222222">      elasticsearch {</span>
<span style="color: #222222">        hosts =&gt; [ "sample-elasticsearch:9200" ]</span>
<span style="color: #222222">        user =&gt; "elastic"</span>
<span style="color: #222222">        password =&gt; "Password1$"</span>
<span style="color: #222222">        index =&gt; "azureeventhub-%{+YYYY.MM.dd}"</span>
<span style="color: #222222">      }</span>
<span style="color: #222222">    }</span>
<span style="color: #222222">  </span><span style="color: #3363a4">logstash.conf</span><span style="color: #222222">:</span><span style="color: #db7100"> |</span></span></code>
<h3>Create a Kubernetes Service</h3>
The next step is to create a Kubernetes Service for Logstash. As you can see in yaml snippet below, port 80 is defined and type is ClusterIP i.e. this service has no external endpoints. Kubernetes will use to selector '<code>component: sample-logstash</code>' to map to the deployment as you are going to see next.

<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> v1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Service</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">type</span><span style="color: #222222">:</span><span style="color: #db7100"> ClusterIP</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">    </span><span style="color: #3363a4">port</span><span style="color: #222222">:</span><span style="color: #db7100"> 80</span>
<span style="color: #222222">    </span><span style="color: #3363a4">targetPort</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span></span></code>
<h3>Create a Kubernetes Deployment</h3>
The next step is to create a Kubernetes Deployment for Logstash. The yaml snippet is displayed below and a few pointers are
<ul>
 	<li><span>The docker image being used to create this resource is <code>docker.elastic.co/logstash/logstash:6.4.1</code></span></li>
 	<li>You can change the number of pods by updating <code>replicas: 1</code></li>
 	<li>Label<code>component: sample-logstash</code> has to match the selector defined in the service</li>
 	<li>ConfigMap defined in previous step is used to mount logstash.yml, logstash.conf, pipelines.yml and azureeventhubs.cfg files i.e. <code>configMap:
name: sample-logstash-configmap</code></li>
</ul>
<code><span> <span style="color: #3363a4">apiVersion</span><span style="color: #222222">:</span><span style="color: #db7100"> apps/v1beta1</span>
<span style="color: #3363a4">kind</span><span style="color: #222222">:</span><span style="color: #db7100"> Deployment</span>
<span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">  </span><span style="color: #3363a4">replicas</span><span style="color: #222222">:</span><span style="color: #db7100"> 1</span>
<span style="color: #222222">  </span><span style="color: #3363a4">selector</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">matchLabels</span><span style="color: #222222">:</span>
<span style="color: #222222">     </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">  </span><span style="color: #3363a4">template</span><span style="color: #222222">:</span>
<span style="color: #222222">    </span><span style="color: #3363a4">metadata</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">labels</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #3363a4">component</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">    </span><span style="color: #3363a4">spec</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #3363a4">containers</span><span style="color: #222222">:</span>
<span style="color: #222222">      </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash</span>
<span style="color: #222222">        </span><span style="color: #3363a4">image</span><span style="color: #222222">:</span><span style="color: #db7100"> docker.elastic.co/logstash/logstash:6.4.1</span>
<span style="color: #222222">        </span><span style="color: #3363a4">volumeMounts</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/config/logstash.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash.yml</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/pipeline/logstash.conf</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> logstash.conf</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/azureeventhubs.cfg</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> azureeventhubs.cfg</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">mountPath</span><span style="color: #222222">:</span><span style="color: #db7100"> /usr/share/logstash/config/pipelines.yml</span>
<span style="color: #222222">          </span><span style="color: #3363a4">subPath</span><span style="color: #222222">:</span><span style="color: #db7100"> pipelines.yml</span>
<span style="color: #222222">        </span><span style="color: #3363a4">resources</span><span style="color: #222222">:</span>
<span style="color: #222222">          </span><span style="color: #3363a4">limits</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 1000m</span>
<span style="color: #222222">          </span><span style="color: #3363a4">requests</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">cpu</span><span style="color: #222222">:</span><span style="color: #db7100"> 100m</span>
<span style="color: #222222">        </span><span style="color: #3363a4">ports</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">containerPort</span><span style="color: #222222">:</span><span style="color: #db7100"> 5601</span>
<span style="color: #222222">          </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> http</span>
<span style="color: #222222">      </span><span style="color: #3363a4">volumes</span><span style="color: #222222">:</span>
<span style="color: #222222">        </span><span style="color: #222222">-</span><span style="color: #222222"> </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span>
<span style="color: #222222">          </span><span style="color: #3363a4">configMap</span><span style="color: #222222">:</span>
<span style="color: #222222">            </span><span style="color: #3363a4">name</span><span style="color: #222222">:</span><span style="color: #db7100"> sample-logstash-configmap</span></span></code>

As mentioned earlier, Logstash will use <code>azure_event_hubs</code> plugin to ingest data into Elasticsearch. You can get installed plugins list by following steps listed below
<ul>
 	<li>Run command <code>kubectl exec -ti {Logstash_Pod_Name}  bash</code> to connect to Logstash POD.</li>
 	<li>Run command <code>bin/logstash-plugin list</code> to see installed plugins</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.32.39-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.32.39-PM.png" alt="" width="269" height="245" class=" wp-image-685 aligncenter" /></a>
<h2>Demo</h2>
After all resources are deployed to AKS, run client app to send messages to Event hub. Open Kibana and you will see ELK stack statistics in Monitoring section. The number of messages sent by client App to Event hub will be displayed in Logstash's Events received and events emitted statistics.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.37.15-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.37.15-PM-1024x533.png" alt="" width="1024" height="533" class="alignnone size-large wp-image-695" /></a>

The Discover tab of Kibana will display events ingested into Elasticsearch once you create index filters e.g. I have selected index filter as 'azureeventhub*' and you can see that this index naming convention was defined in azureeventshub.cfg pipeline<code>index =&gt; "azureeventhub-%{+YYYY.MM.dd}</code>.<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.36.34-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-23-at-10.36.34-PM-1024x453.png" alt="" width="1024" height="453" class="alignnone size-large wp-image-705" /></a>

&nbsp;

You can download source code for this article from<span> </span><a target="_blank" href="https://github.com/atverma/AzureKubernetesService-ELK" rel="noopener">GitHub repository</a>

&nbsp;

The second part of this series is <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/10/09/azure-kubernetes-service-aks-azure-ad-saml-based-single-sign-on-to-secure-elasticsearch-and-kibana-and-securing-communications-in-elk" rel="noopener">Azure Kubernetes Service (AKS): Azure AD SAML based Single Sign on to secure Elasticsearch and Kibana and securing communications in ELK</a>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Azure Dev Spaces - Debug Containers directly in Azure Kubernetes Service (AKS) from Visual Studio 2017</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-dev-spaces-debug-containers-directly-in-azure-kubernetes-service-aks-from-visual-studio-2017</link>
      <pubDate>Wed, 19 Sep 2018 03:34:39 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=495</guid>
      <description><![CDATA[This is second part of the series on Azure Dev Spaces. The first part of this services describes...]]></description>
      <content:encoded><![CDATA[This is second part of the series on Azure Dev Spaces. The <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/18/azure-dev-spaces-deploying-angular-and-asp-net-core-app-to-azure-kubernetes-service-aks/" rel="noopener">first part of this services</a> describes steps needed to deploy Angular App and ASP.net Core Web API to AKS using Azure Dev Spaces. This article will show ease of debugging containers deployed in AKS from Visual Studio 2017.

In the first part of this series, I had created a sample ASP.net Core Web API app and deployed that to AKS using Azure Dev Spaces. The steps needed to debug ASP.net Core Web API application running in AKS are
<ul>
 	<li>Install<span> </span><a href="https://aka.ms/get-azds-visualstudio">Visual Studio Tools for Kubernetes</a><span>. Once installation completes, open 'Tools -&gt; Extensions and Updates' in Visual Studio and update this extension for any updates.</span></li>
 	<li>Open SampleWebApp solution in Visual Studio. The source code can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureDevSpaces-Angular-AspnetCore-SQLServer/tree/master/samplewebapp" rel="noopener">GitHub</a>.</li>
 	<li>Select Debug target as Azure Dev Spaces</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.15.19-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.15.19-PM-1024x273.png" alt="" width="627" height="167" class="wp-image-525 aligncenter" /></a>
<ul>
 	<li>Add a breakpoint in Get method of UsersController which we are going to debug</li>
 	<li>Start Debugging and select AKS cluster and Space in Azure Dev Spaces prompt</li>
 	<li><a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.15.44-PM1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.15.44-PM1.png" alt="" width="617" height="418" class="wp-image-535 aligncenter" /></a>You can get public URL from Output window by selecting Azure Dev Spaces as displayed below</li>
 	<li><a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.42.31-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.42.31-PM-1024x261.png" alt="" width="636" height="162" class="wp-image-545 aligncenter" /></a>Open https://{URL}/api/users in browser and request will hit the breakpoint added previously</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.54.40-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-10.54.40-PM-1024x258.png" alt="" width="638" height="161" class="wp-image-555 aligncenter" /></a>

You can check the status of URL by running command <code>azds list-uris</code>. If URL isn't available for more than 5 minutes you will need to delete PODS as explained in previous article. This completes this article which showed how easy it is to debug containers deployed in AKS from Visual Studio 2017 using Azure Dev Spaces.]]></content:encoded>
    </item>
    <item>
      <title>Azure Dev Spaces - Deploying Angular and ASP.net Core App to Azure Kubernetes Service (AKS)</title>
      <link>https://docs.microsoft.com/archive/blogs/atverma/azure-dev-spaces-deploying-angular-and-asp-net-core-app-to-azure-kubernetes-service-aks</link>
      <pubDate>Tue, 18 Sep 2018 06:07:57 GMT</pubDate>
      <dc:creator><![CDATA[Atul.Verma]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/atverma/?p=395</guid>
      <description><![CDATA[This article will show the ease of running and deploying containers in Azure Kubernetes Service...]]></description>
      <content:encoded><![CDATA[This article will show the ease of running and deploying containers in Azure Kubernetes Service (AKS) with minimal dev machine setup with Azure Dev Spaces. I am going to reuse sample application components from my <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/16/azure-kubernetes-service-aks-deploying-angular-asp-net-core-and-sql-server-on-linux/" rel="noopener">previous article on AKS</a>. <span>With Azure Dev Spaces, you can develop on Windows, Mac, or Linux using familiar tools like Visual Studio, Visual Studio Code, or the command line.</span> I have chosen Visual Studio Code on Mac.

In this article, I will share the steps needed to deploy sample application I created in Angular and ASP.net Core Web API using Azure Dev Spaces.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-15-at-10.57.01-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-15-at-10.57.01-PM-1024x626.png" alt="" width="259" height="158" class="alignnone wp-image-345" /></a>

At time of writing this article Azure Dev Spaces is in preview and is isn't available in all regions.
<h2>Setting up Azure Dev Spaces</h2>
If you don't have Azure Kubernetes Service (AKS) cluster running, you can go through this article to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/aks/create-cluster" rel="noopener">Create AKS Cluster</a>.    Once AKS cluster is ready, the following commands are needed to setup Azure Dev Spaces
<ul>
 	<li>For Visual Studio code, I had to install <a href="https://marketplace.visualstudio.com/items?itemName=azuredevspaces.azds">Azure Dev Spaces extension</a></li>
 	<li><span>Run this Command to setup Azure Dev Spaces: <code>az aks use-dev-spaces -g {ResourceGroup} -n {AKSClusterName}</code></span>
<ul>
 	<li>Update {ResourceGroup} and {AKSClusterName}</li>
 	<li>Confirmation and admin privileges will be needed to proceed with installation</li>
 	<li>Confirmation to create an Azure Dev Spaces Controller</li>
 	<li><span>Select a dev space or Kubernetes namespace to use as a dev space</span></li>
 	<li>Confirmation to select Kubernetes namespace 'default' as a dev space</li>
</ul>
</li>
</ul>
This command is going to take a while. After completion if you browse to Azure Portal, Azure Dev Spaces Controller resource will be created.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-17-at-1.07.16-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-17-at-1.07.16-PM-300x160.png" alt="" width="431" height="230" class="alignnone wp-image-405" /></a>

You can go through this article to<a target="_blank" href="https://docs.microsoft.com/en-us/azure/dev-spaces/" rel="noopener"> Setup Azure Dev Spaces</a> for detailed information.

Similar to previous article, this sample application consists of Angular app which loads data from ASP.net Core WebAPI and Web API loads data from SQL Server DB.
<h2>SQL Server</h2>
This article assumes that SQL Server DB is already deployed to AKS. You can go through steps listed in <a target="_blank" href="https://blogs.msdn.microsoft.com/atverma/2018/09/16/azure-kubernetes-service-aks-deploying-angular-asp-net-core-and-sql-server-on-linux/" rel="noopener">previous article to setup SQL Server.</a> The source code link of yaml snippets needed to deploy SQL Server can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureDevSpaces-Angular-AspnetCore-SQLServer/tree/master/sqlserver" rel="noopener">GitHub</a>.

After creating all the resources needed to configure SQL Server, the next step is to setup ASP.net Core Web API.
<h2>ASP.net Core Web API</h2>
<span>You can view source code for this solution at </span><a target="_blank" href="https://github.com/atverma/AzureDevSpaces-Angular-AspnetCore-SQLServer/tree/master/samplewebapp" rel="noopener">GitHub</a><span>. Please update </span><code>DatabaseConnectionString</code><span> setting in 'appsettings.json' file with the SQL Server database connection string based on your 'sa' user password. This Web.API uses EntityFrameworkCore to create database and seed sample data. There is only one method in the Controller to get list of users from database to keep things simple.</span>

As you can see, I haven't added DockerFile or scripts needed to deploy web api to AKS yet.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.00.34-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.00.34-AM-300x276.png" alt="" width="368" height="339" class="alignnone wp-image-435" /></a>

The next step is show the magic of Azure Dev Spaces extension which I had installed for VS Code. The steps are
<ul>
 	<li>Open SampleWebApp project folder in Terminal (Right click and select Open in Terminal is using VS Code)</li>
 	<li>Run Command <code>azds prep --public</code>. This command is going to add files needed to deploy this resource to Kubernetes.</li>
 	<li><a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.22.14-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.22.14-AM-300x125.png" alt="" width="404" height="168" class="alignnone wp-image-445" /></a></li>
 	<li>Run Command <code>azds up<code></code></code> to start deploying this Web API to AKS.  This is going to take a while and steps involved are
<ul>
 	<li>Synchronizing Files</li>
 	<li>Building Helm Chart</li>
 	<li>Building Container Image</li>
 	<li>Deploying Image to AKS</li>
</ul>
</li>
</ul>
As you can see below, ASP.net Web API is up and running and main pointers are
<ul>
 	<li>Azure Dev Spaces will create two URLs one for ingress and other as tunneled as you can see in first two lines of this image. The 'http' ingress URL is going to take a while and if it takes more than 5 minutes which happened in my case too, you will need to delete the pods by running commands <code>kubectl delete pod -n kube-system -l app=addon-http-application-routing-external-dns</code> and
<code>kubectl delete pod -n kube-system -l app=addon-http-application-routing-nginx-ingress</code>. Please read <a target="_blank" href="https://docs.microsoft.com/en-us/azure/dev-spaces/troubleshooting" rel="noopener">Troubleshooting</a> for common problems.</li>
 	<li>The Command to check list of Uris and Status is <code>azds list-uris</code></li>
 	<li>Browse the URL to see list of users i.e. https://{YOUR_HTTP_URL}/api/users</li>
 	<li>The pod name is also displayed and you can run this command to view details <code>kubectl exec -ti {POD_NAME} bash</code></li>
 	<li>As you can see in image captured below, you can see info messages which provides further insights</li>
 	<li>Disclaimer: I tried to debug which is another cool feature however I ran into weird issue in VS Code. You can read futher about debugging at <a target="_blank" href="https://docs.microsoft.com/en-us/azure/dev-spaces/get-started-netcore#get-kubernetes-debugging-for-vs-code" rel="noopener">Initialize debug assets with the VS Code extension</a>.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.27.21-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.27.21-AM-1024x684.png" alt="" width="697" height="465" class="alignnone wp-image-455" /></a>

The next component is Angular App. Please take a note of the URL of this Web API as it is needed to be updated in Angular App.
<h2>Angular App</h2>
This is the final component and before we proceed, we need to update external endpoint value of the Web API Service in Angular App. You can view the source code of Angular App at <a target="_blank" href="https://github.com/atverma/AzureDevSpaces-Angular-AspnetCore-SQLServer/tree/master/sampleangularapp" rel="noopener">GitHub</a>.

You will need to update environment.ts and environment.prod.ts to update <code>API_URL</code>

<code>export const environment = {
ApplicationConfig: {
'API_URL': 'https://{YOUR_WEB_API_ENDPOINT}/api/'
}
};</code>

As you can see, I haven't added DockerFile or scripts needed to deploy web api to Angular App yet.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.01.09-AM1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-12.01.09-AM1-599x1024.png" alt="" width="249" height="426" class="alignnone wp-image-465" /></a>

The differences which this Angular app code has from previous article's Angular app are
<ul>
 	<li>addition of 'server.js' file so that this Angular App can be hosted in nodejs without need for nginx. In 'package.json', I have updated <code><span>"start"</span><span>: </span><span>"node server.js"</span></code>. The 'server.js' code snippet is</li>
 	<li><code>var express = require('express');
var app = express();
app.use(express.static(__dirname + '/dist/sampleangularapp'));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/dist/sampleangularapp/index.html');
});
app.get('/api', function (req, res) {
res.send(__dirname);
});
var port = process.env.PORT || 80;
var server = app.listen(port, function () {
console.log('Listening on port ' + port);
});</code></li>
</ul>
<ul>
 	<li>addition of <code>RUN npm run build -- --prod</code> command after auto-generating Dockerfile (created by running <code>azds prep --public</code> command) since I have used '<code>/dist/sampleangularapp</code>' path in 'server.js'.</li>
</ul>
Subsequent steps needed to deploy Angular App to AKS using Azure Dev Spaces are similar to ASP.net Web API i.e.
<ul>
 	<li>Open sampleangularapp project folder in Terminal (Right click and select Open in Terminal is using VS Code)</li>
 	<li>Run Command <code>azds prep --public</code>. This command is going to add files needed to deploy this resource to Kubernetes</li>
 	<li>Run Command <code>azds up</code> to start deploying this Web API to AKS.  This is going to take a while.</li>
 	<li>Open the Http URL once ingress endpoint status is Available.</li>
 	<li><a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-16-at-1.15.59-PM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-16-at-1.15.59-PM-300x124.png" alt="" width="375" height="155" class="alignnone wp-image-355" /></a></li>
</ul>
You can see the Azure Dev Spaces has created ingresses(samplewebapp and sampleangularapp) by opening AKS Dashboard. If you don't want to create ingresses you can set <code><span style="font-family: Menlo"><span>ingress</span><span>: </span><span>enabled</span><span>:</span><span> false</span></span><span></span></code> in helm charts values.yaml.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-1.39.09-AM.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/Screen-Shot-2018-09-18-at-1.39.09-AM-1024x341.png" alt="" width="643" height="214" class="alignnone wp-image-475" /></a>
<h2><strong>Summary</strong></h2>
In this article I have provided insights about Azure Dev Spaces(still in preview). The source code for this complete article can be downloaded from <a target="_blank" href="https://github.com/atverma/AzureDevSpaces-Angular-AspnetCore-SQLServer" rel="noopener">GitHub</a>.]]></content:encoded>
    </item>
  </channel>
</rss>