<?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>« Je connais mes limites. C'est pourquoi je vais au-delà. »</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/stephgou/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/stephgou/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:31:15 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/configuring-the-azure-kubernetes-infrastructure-with-bash-scripts-and-ansible-tasks</link>
      <pubDate>Sat, 27 Aug 2016 13:38:39 GMT</pubDate>
      <dc:creator><![CDATA[Stéphane GOUDEAU]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/?p=1765</guid>
      <description><![CDATA[This article is the fourth one about a solution we have built with my friend Hervé Leclerc (Alter...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal" style="background: white;margin: 0cm 0cm 8pt"><span lang="EN" style="color: black"><font size="3" face="Calibri">This article is the fourth one about a solution we have built with my friend </font></span><span lang="EN-US"><a href="https://twitter.com/hleclerc"><span lang="EN"><u>Hervé Leclerc</u></span></a></span><span lang="EN" style="color: black"><font size="3" face="Calibri"> (</font></span><span lang="EN-US"><a href="https://www.alterway.fr/"><span lang="EN"><u>Alter Way</u></span></a></span><span lang="EN" style="color: black"><font size="3" face="Calibri"> CTO) in order to automate a </font></span><span lang="EN-US"><a href="https://github.com/DXFrance/AzureKubernetes"><span lang="EN"><u>Kubernetes Cluster deployment on Azure</u></span></a></span><font size="3"><font face="Calibri"><span lang="EN" style="color: black">:</span></font></font></p> <p class="MsoNormalCxSpFirst" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Calibri"> </font></span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/11/kubernetes-cluster-automated-deployment-on-azure-first-step"><span lang="EN"><u>Kubernetes and Microsoft Azure</u></span></a></span></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/15/kubernetes-cluster-automated-deployment-on-azure-programmatic-scripting"><span lang="EN"><u>Programmatic scripting of Kubernetes deployment</u></span></a></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/19/provisioning-the-azure-kubernetes-infrastructure-with-a-declarative-template/"><span style="color: #0563c1"><u><font size="3">Provisioning the Azure Kubernetes infrastructure with a declarative template</font></u></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/27/configuring-the-azure-kubernetes-infrastructure-with-bash-scripts-and-ansible-tasks/"><span style="text-decoration: none;color: #337ab7"><font size="3">Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks</font></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Automating the Kubernetes UI dashboard configuration</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Using Kubernetes…</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 8pt"> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">As we have seen previously, it’s possible to use ARM extension to execute commands on the provisioned machines. In order to automate the required setup on each machine, we may consider different options. We could manage the cluster configuration with a collection of scripts but the more efficient way is surely to keep a declarative logic instead of a pure programming approach. What we need is a consistent, reliable, and secure way to manage the Kubernetes cluster environment configuration without adding extra complexity due to an automation framework. That’s why we’ve chosen Ansible.</font></font></font></span></p></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Ansible role in Kubernetes configuration</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible is a solution for configuration management based on a state-driven resource model that describes the desired state of the target, not the paths to get them to this state. Ansible role is to transform the environment to the desired state and to allow reliable and repeatable tasks without the failures risks related to scripting. So in order to handle the Kubernetes configuration, we have provisioned a dedicated Ansible controller which role is to enact configuration on the different type of nodes.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image747.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb577.png" width="1033" height="581"></a></font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Ansible playbooks</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible automation jobs are declared with a simple description language (YAML), both human-readable and machine-parsable, in what is called an Ansible playbook. </font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN"><font color="#1f4d78"><font face="Calibri Light">Ansible playbook implementation</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">Playbooks are the entry point of an Ansible provisioning. They contain information about the systems where the provisioning should be executed, as well as the directives or steps that should be executed. They can orchestrate steps of any ordered process, even as different steps must bounce back and forth between sets of machines in particular orders. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">Each playbook is composed of one or more “plays” in a list, which goal is to map a group of hosts to some well-defined roles (in our Kubernetes cluster: “master”, “node”, “etcd”) represented by tasks that call Ansible modules. By composing a playbook of multiple “plays”, it is possible to orchestrate multi-machine deployments. In our context, Ansible playbook enables to orchestrate multiple slices of the Kubernetes infrastructure topology, with very detailed control over how many machines to tackle at a time. So one of the critical task of our implementation was to create these playbooks.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">When Hervé and I began to work on this automated Ansible CentOS Kubernetes deployment, we checked if there weren’t any existing asset we should build the solution upon. And we found exactly what we were looking for with the repository “Deployment automation of Kubernetes on CentOS 7” on GitHub (</font></span><a href="https://github.com/xuant/ansible-kubernetes-centos"><span lang="EN-US"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/xuant/ansible-kubernetes-centos</font></u></span></a><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">)</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font color="#000000"><font size="3"><font face="Calibri"><i><span lang="EN">Note: Hervé did a fork from this </span></i><i><span lang="EN-US">git repository in order to enable further modification (</span></i></font></font></font><a href="https://github.com/herveleclerc/ansible-kubernetes-centos.git"><i><span lang="EN-US"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/herveleclerc/ansible-kubernetes-centos.git</font></u></span></i></a><font color="#000000"><font size="3"><font face="Calibri"><i><span lang="EN-US">) and I made a submodule </span></i><i><span lang="EN">["ansible-kubernetes-centos"] from this fork in order to clone this Git repository and keep it as a subdirectory of our main Git repository (to keep commits separate).</span></i></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Here's what the main playbook (</font></span><a href="https://github.com/herveleclerc/ansible-kubernetes-centos/blob/master/integrated-wait-deploy.yml"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/herveleclerc/ansible-kubernetes-centos/blob/master/integrated-wait-deploy.yml</font></u></span></a><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">) currently looks like:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN" style="font-size: 9.5pt;font-family: consolas;background: white;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">---</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: cluster</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>gather_facts: false</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>tasks:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>- name: "Wait for port 3333 to be ready"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>local_action: wait_for port=3333 host="{{ inventory_hostname }}" state=started connect_timeout=2 timeout=600</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: cluster</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- common</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- kubernetes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: etcd</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- etcd</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: masters</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- master</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: minions</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- minion</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: masters</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- flannel-master</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black">- hosts: minions</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp; </span>roles:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>- flannel-minion</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9pt;font-family: consolas;color: black"></span>&nbsp;</p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN"><font color="#1f4d78"><font face="Calibri Light">Ansible playbook deployment</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible allows you to act as another user (through the “become: yes” declaration), different from the user that is logged into the machine (remote user). This is done using existing privilege escalation tools like sudo. As the playbook is launched through the ARM custom extension, the default requirement for a “tty” has to be disabled for the sudo users (and after playbook execution, to be re-enabled). The corresponding code that triggers the “integrated-wait-deploy.yml” playbook deployment is given with the following function:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">deploy()</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>sed -i </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">'s/Defaults<span>&nbsp;&nbsp;&nbsp; </span>requiretty/Defaults<span>&nbsp;&nbsp;&nbsp; </span>!requiretty/g'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> /etc/sudoers</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>ansible-playbook -i </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_HOST_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> integrated-wait-deploy.yml | tee -a /tmp/deploy-</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${LOG_DATE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.log</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>sed -i </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">'s/Defaults<span>&nbsp;&nbsp;&nbsp; </span>!requiretty/Defaults<span>&nbsp;&nbsp;&nbsp; </span>requiretty/g'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> /etc/sudoers</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN"><font color="#000000" size="3" face="Calibri">&nbsp;</font></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Solution environment setup</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">This Kubernetes nodes and the Ansible controller should have the same level of OS packages. </font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Ansible controller and nodes software environment</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">The software environment of the virtual machines involved in this Kubernetes cluster is setup through the usage of the Open Source EPEL (Extra Packages for Enterprise Linux) Linux repository (a repository of add-on packages that complements the Fedora-based Red Hat Enterprise Linux (RHEL) and its compatible spinoffs, such as CentOS and Scientific Linux).</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Current EPEL version is the following:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">EPEL_REPO=<a href="https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm"><u><font color="#0563c1">https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm</font></u></a></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"><em>Please note that sometimes a new EPEL release is published without any guaranty for the previous one retention. So a current limitation with our current template is the requirement to update this EPEL url when there’s a new version for software packages hosted on EPEL repository.</em> </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">The version of the EPEL repository is specified in “ansible-config.sh” and “node-config.sh” scripts with the following function:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">install_epel_repo()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp; </span>rpm -iUvh </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${EPEL_REPO}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">&nbsp;</font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Ansible controller setup</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible setup is achieved through a simple clone from GitHub and a compilation of the corresponding code.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Here is the code of the corresponding function:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">install_ansible()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>rm -rf ansible</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>git clone </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">https://github.com/ansible/ansible.git </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">--depth 1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cd ansible || error_log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Unable to cd to ansible directory"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>git submodule update --init --recursive</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>make install</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">The </span><span lang="EN">“make install”</span><span lang="EN"> that does the compilation first requires “Development Tools groups” to be installed.</span><span lang="EN"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">install_required_groups()</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>until yum -y group install </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Development Tools"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>do</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Lock detected on VM init Try again..." "N"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>sleep 2</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>done</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Ansible configuration</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Ansible configuration is defined in two files, the “hosts” inventory file and the “ansible.cfg” file. The Ansible control path has to be shorten to avoid errors with long host names, long user names or deeply nested home directories. The corresponding code for the “ansible.cfg” creation is the following:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span style="font-size: 9.5pt;font-family: consolas;color: #7030a0">configure_ansible()</span><span style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>rm -rf /etc/ansible</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>mkdir -p /etc/ansible</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cp examples/hosts /etc/ansible/.</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[defaults]\ndeprecation_warnings=False\n\n" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">&gt;&gt; </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_CONFIG_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[defaults]\nhost_key_checking = False\n\n"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> &gt;&gt; </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_CONFIG_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>echo </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$'[ssh_connection]\ncontrol_path = ~/.ssh/ansible-%%h-%%r'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> &gt;&gt; <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_CONFIG_FILE}"<span>&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"\npipelining = True\n" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">&gt;&gt; </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_CONFIG_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span><span lang="EN"></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Ansible connection management</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible represents the machines it manages using a simple INI file that organizes the Kubernetes nodes in groups corresponding to their role. In our solution we created “etcd”,”master”,”minion” groups completed by a “cluster” role that gathers all the nodes.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">Here's what the plain text inventory file (“</span><span lang="EN-US">/etc/ansible/hosts”) looks lik</span><span lang="EN">e:</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image748.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb578.png" width="1080" height="243"></a></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">This inventory has to be dynamically created depending on the cluster size requested in the ARM template.</font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><font color="#1f4d78"><font face="Calibri Light"><span lang="EN">Inventory based on Kubernetes nodes dynamically generated private </span><span lang="EN-US">IP</span></font></font></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">In our </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/Kubernetes-Ansible-Centos-Azure/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">first ARM template implementation</font></u></a></span><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">, each Kubernetes node launches a short “first-boot.sh” custom script in order to specify its dynamically generated private IP and its role in the cluster.<u><span style="color: #0563c1"></span></u></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">privateIP=$1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">role=$2</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">FACTS=/etc/ansible/facts</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">mkdir -p $FACTS</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">echo </span><span style="font-size: 9.5pt;font-family: consolas;color: red">"${privateIP},${role}"</span><span style="font-size: 9.5pt;font-family: consolas;color: black"> &gt; $FACTS/private-ip-role.fact </span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">chmod 755 /etc/ansible</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">chmod 755 /etc/ansible/facts</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">chmod a+r $FACTS/private-ip.fact </span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">exit 0</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">In this version, the </span><span lang="EN">“deploy.sh” script makes ssh requests to nodes to get their private IP in order to create the inventory file. Here is an extract of the code of the corresponding function:</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">get_private_ip()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>let numberOfMasters=$numberOfMasters-1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>for i in </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(seq 0 $numberOfMasters)</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>do</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>let j=4+$i</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>su - </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${sshu}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> -c </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"ssh -l ${sshu} ${subnetMasters3}.${j} cat $FACTS/private-ip-role.fact"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> &gt;&gt; /tmp/hosts.inv </span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>done</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US">#...and so on for etcd, and minions nodes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN"><font color="#000000" size="3" face="Calibri">&nbsp;</font></span></p> <h3 style="margin: 2pt 0cm 0pt"><font color="#1f4d78"><font face="Calibri Light"><span lang="EN">Inventory based on Kubernetes nodes static ARM computed </span><span lang="EN-US">IP</span></font></font></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">In our </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">second ARM implementation</font></u></a><font size="3"><font color="#000000"><font face="Calibri"> (the “linked” template composed from several ones), private IPs are computed directly in the ARM script. The following code is extracted from “KubeMasterNodes.json”:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"resources"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: [</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"apiVersion"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[parameters('apiVersion')]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"type"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"Microsoft.Network/networkInterfaces"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"name"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[concat(parameters('kubeMastersNicName'), copyindex())]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"location"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[resourceGroup().location]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"copy"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: {</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"name"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[variables('kubeMastersNetworkInterfacesCopy')]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"count"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[parameters('numberOfMasters')]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>},</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"properties"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: {</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"ipConfigurations"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: [</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"name"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"MastersIpConfig"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"properties"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: {</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"privateIPAllocationMethod"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"Static"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><b><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></b><b><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"privateIPAddress"</span></b><b><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span></b><b><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[concat(parameters('kubeMastersSubnetRoot'), '.',add(copyindex(),4) )]"</span></b><b><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></b></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"subnet"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: {</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"id"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[parameters('kubeMastersSubnetRef')]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>},</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"loadBalancerBackendAddressPools"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: [</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"id"</span><span style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[parameters('kubeMastersLbBackendPoolID')]"</span><span style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span>}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>],</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"loadBalancerInboundNatRules"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: [</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2e75b6">"id"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">: </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">"[concat(parameters('kubeMastersLbID'),'/inboundNatRules/SSH-', copyindex())]"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>]</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp; </span>]</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%"><span>&nbsp;&nbsp;&nbsp; </span>},</span><span lang="EN"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"><span>&nbsp;</span>In the “ansible-config.sh” custom script file, there is the function that creates this inventory:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">create_inventory()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>masters=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">""</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>etcd=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">""</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>minions=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">""</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">for</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> i </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">in </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(cat /tmp/hosts.inv)</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">do</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>ip=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(echo "$i"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">|</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">cut -f1 -d,)</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>role=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(echo "$i"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">|</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">cut -f2 -d,)</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">if </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">[ </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$role" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">= </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"masters"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> ]; </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">then</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>masters=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(printf "%s\n%s" "${masters}" "${ip} ansible_user=${ANSIBLE_USER} ansible_ssh_private_key_file=/home/${ANSIBLE_USER}/.ssh/idgen_rsa"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">elif </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">[ </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$role" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas"><font color="#000000">=</font><span style="color: red"> "minions"</span><span style="color: black"> ]; </span><span style="color: fuchsia">then</span><span style="color: black"></span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>minions=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">$(printf "%s\n%s" "${minions}" "${ip} ansible_user=${ANSIBLE_USER} ansible_ssh_private_key_file=/home/${ANSIBLE_USER}/.ssh/idgen_rsa"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">elif </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">[ </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$role" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas"><font color="#000000">=</font><span style="color: red"> "etcd"</span><span style="color: black"> ]; </span><span style="color: fuchsia">then</span><span style="color: black"></span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>etcd</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas"><font color="#000000">=</font><span style="color: red">$(printf "%s\n%s" "${etcd}" "${ip} ansible_user=${ANSIBLE_USER} ansible_ssh_private_key_file=/home/${ANSIBLE_USER}/.ssh/idgen_rsa"</span><span style="color: black">)</span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">fi</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: fuchsia">done</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[cluster]%s\n" "${masters}${minions}${etcd}" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">&gt;&gt;</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"> "${ANSIBLE_HOST_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[masters]%s\n" "${masters}" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">&gt;&gt;</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"> "${ANSIBLE_HOST_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[minions]%s\n" "${minions}" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">&gt;&gt;</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"> "${ANSIBLE_HOST_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"[etcd]%s\n" "${etcd}"<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: gray">&gt;&gt;</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"> "${ANSIBLE_HOST_FILE}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">As you may have noticed, this file completes the IP addresses with a path for the SSH private key.</font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN"><font color="#1f4d78"><font face="Calibri Light">Ansible security model</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Ansible does not require any remote agents to deliver modules to remote systems and to execute tasks. It respects the security model of the system under management by running user-supplied credentials and as we’ve seen includes support for “sudo”. To secure the remote configuration management system, Ansible may also rely on the default transport layer through the usage of OpenSSH, which is more secure than password authentication. This is the way we decided to secure our deployment.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN"><font color="#000000" size="3" face="Calibri">In our </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">second ARM implementation</font></u></a><font size="3"><font color="#000000"><font face="Calibri"> </font></font></font></span>SSH key generation is done through a simple call to “ssh-keygen” (<span lang="EN-US"><font color="#000000" size="3" face="Calibri">In the </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/Kubernetes-Ansible-Centos-Azure/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">first one</font></u></a></span><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">, we use existing keys).</font></font></font></span></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">generate_sshkeys()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>echo -e </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">'y\n'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">|ssh-keygen -b 4096 -f idgen_rsa -t rsa -q -N </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">''</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Then the configuration is done by copying the generated key file in the .ssh folder for “root” and “Ansible” users and by changing their permissions.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">ssh_config()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>printf </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Host *\n<span>&nbsp; </span>user %s\n<span>&nbsp; </span>StrictHostKeyChecking no\n" "${ANSIBLE_USER}"<span>&nbsp; </span>&gt;&gt; "/home/${ANSIBLE_USER}/.ssh/config"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cp idgen_rsa </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/home/${ANSIBLE_USER}/.ssh/idgen_rsa"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cp idgen_rsa.pub </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/home/${ANSIBLE_USER}/.ssh/idgen_rsa.pub"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>chmod 700 </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/home/${ANSIBLE_USER}/.ssh"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>chown -R </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${ANSIBLE_USER}:" "/home/${ANSIBLE_USER}/.ssh"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>chmod 400 </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/home/${ANSIBLE_USER}/.ssh/idgen_rsa"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>chmod 644 </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/home/${ANSIBLE_USER}/.ssh/idgen_rsa.pub"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">We also have to define a way to propagate SSH keys between Ansible controller and Kubernetes nodes. At least the SSH public key is stored in the </font><strong><span>.ssh/authorized_keys</span></strong><font face="Calibri"> file on all the computers Ansible has to log in to while the private key is kept on the computer the Ansible user log in from (in fact we have automated the copy of both public and private keys, because having the ability to SSH from any machine to the other was sometimes quite useful during development phase…). In order to do this, we use Azure Storage. </font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN"><font color="#1f4d78"><font face="Calibri Light">SSH Key files sharing</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">The “ansible-config.sh” file launched in Ansible controller generates the SSH keys and stores them in a blob in an Azure storage container with the “WriteSSHToPrivateStorage.py” Python script.</span><span lang="EN" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">put_sshkeys()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;</span>{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Push ssh keys to Azure Storage" "N"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>python WriteSSHToPrivateStorage.py </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${STORAGE_ACCOUNT_NAME}" "${STORAGE_ACCOUNT_KEY}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> idgen_rsa</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>error_log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Unable to write idgen_rsa to storage account ${STORAGE_ACCOUNT_NAME}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>python <b>WriteSSHToPrivateStorage.py</b> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${STORAGE_ACCOUNT_NAME}" "${STORAGE_ACCOUNT_KEY}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> idgen_rsa.pub</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>error_log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Unable to write idgen_rsa.pub to storage account ${STORAGE_ACCOUNT_NAME}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Here is the corresponding “WriteSSHToPrivateStorage.py” Python function:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">import</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">from</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">azure</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">storage</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">blob</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">import</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> BlockBlobService</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">from</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">azure</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">storage</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">blob</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">import</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> ContentSettings</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">block_blob_service = BlockBlobService(account_name=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[1]), account_key=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[2]))</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">block_blob_service.create_container(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">'keys'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">block_blob_service.create_blob_from_path(</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">'keys'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[3]),</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.path.join(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.getcwd(),</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[3])),</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>content_settings=ContentSettings(content_type=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">'text'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">The “node-config.sh” file launched in Ansible controller gets the SSH key files in Azure storage with the “ReadSSHToPrivateStorage.py” Python script which code is the following:</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">import</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">from</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">azure</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">storage</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">blob</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: blue">import</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> BlockBlobService</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">blob_service = BlockBlobService(account_name=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[1]), account_key=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[2]))</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">blob = blob_service.get_blob_to_path(</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #a31515">'keys'</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">,</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[3]),</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.path.join(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">os</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.getcwd(),</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #2b91af">str</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">(</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #6f008a">sys</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">.argv[3])),</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>max_connections=8</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">)</span><span lang="EN"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">These two Python functions require the corresponding Python module to be installed on the Ansible controller and Kubernetes nodes. One easy way to do it is first to install “pip”. Pip is a package management system used to install and manage software packages written in Python. Here is the code corresponding to this setup.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">install_required_packages()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>until yum install -y git python2-devel python-pip libffi-devel libssl-dev openssl-devel</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>do</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>sleep 2</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>done</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span style="font-size: 9.5pt;font-family: consolas;color: black">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">install_python_modules()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>pip install PyYAML jinja2 paramiko</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>pip install --upgrade pip</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>pip install azure-storage</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">But providing the technical mechanism to share SSH keys files between Kubernetes nodes and Ansible controller is not sufficient. We have to define how they both interact with each other.</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%"></span></font></font></font></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Synchronization between Ansible controller and nodes</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">One of the complex requirements of the solution we propose is the way we handle the synchronization of the different configuration tasks.</font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">ARM “depensOn” and SSH key files pre-upload solution</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">In our </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/Kubernetes-Ansible-Centos-Azure/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">first ARM template implementation</font></u></a></span><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">, this is easily handled through the usage of “dependsOn” attribute in the ARM template. This solution requires all the nodes to be deployed before launching the Ansible playbook with a custom “deploy.sh” script extension. This script is deployed on the Ansible controller with the “fileUris” settings (“scriptBlobUrl”). The others filesUris correspond to the SSH key files used for Ansible secure connection management (and another token file used for monitoring through Slack). </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">In this solution, synchronization between Ansible controllers and node is quite easy but requires the publication of SSH key files in an Azure storage account (or an Azure Key vault) before launching the template deployment, so Ansible controller may download them before running the playbooks. If these files are not deployed before launching the deployment, it will fail. Here are the corresponding files displayed with the Azure Management Studio tool.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image749.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb579.png" width="1120" height="817"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt">&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">The following code is extracted from the “azuredeploy.json” and corresponds to the Ansible extension provisioning.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image750.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb580.png" width="1107" height="937"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN"></span></font></font></font></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Netcat synchronization and automated Python SSH key files sharing through Azure Storage </font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">The </font><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json"><u><font color="#0563c1" size="3" face="Calibri">second ARM implementation</font></u></a><font size="3"><font color="#000000"><font face="Calibri"> (the “linked” template composed from several ones) enables to avoid to pre-publish keys in a vault by directly generating these keys during deployment. As this solution may appear more elegant and more secure, by specializing a generated SSH key for Ansible configuration management instead of sharing existing keys for other purpose, it adds a significant level of complexity, because it requires now to handle in parallel both Ansible controller and Kubernetes nodes configuration. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">The bash script on the Ansible controller generates SSH keys but have to wait for the nodes to get theses keys and declared them as authorized for logon before launching the Ansible playbook. A solution to this kind of scenario may be implemented thanks to the usage of Netcat, a networking utility using the TCP/IP protocol, which reads and writes data across network connections, and that can be used directly or easily driven by other programs and scripts. The way we synchronized both scripts “config-ansible.sh” and “config-node.sh” is described with the following schema.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image752.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb581.png" width="1118" height="508"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">So the first machines which have to wait are the nodes. As they are waiting for getting a file, it’s quite simple to have them making this request periodically in pull mode until the file is there (with a limit on attempts number). The code extracted from “config-node.sh” is the following.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">get_sshkeys()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;</span>{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span>c=0;</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp; </span>sleep 80</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">until</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> <b>python GetSSHFromPrivateStorage.py</b> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${STORAGE_ACCOUNT_NAME}" "${STORAGE_ACCOUNT_KEY}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> <b>idgen_rsa</b></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">do</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Fails to Get idgen_rsa key trying again ..." "N"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sleep 80</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>let c=${c}+1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">if</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> [ "${c}" -gt 5 ]; </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">then</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Timeout to get idgen_rsa key exiting ..." "1"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>exit 1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">fi</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #e523c9">done</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span><b>python GetSSHFromPrivateStorage.py</b> </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"${STORAGE_ACCOUNT_NAME}" "${STORAGE_ACCOUNT_KEY}"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"> <b>idgen_rsa.pub</b></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span><span lang="EN"></span></p> <p><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">In order to launch the Netcat server in the background (using the &amp; command at the end of the line), and logout from the “config-node.sh” extension script session without the process being killed, the job is executed with “nohup” command (which stands for “no hang up”). </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">To make sure that the Netcat server launching will not interfere with foreground commands and will continue to run after the extension script logouts, it’s also required to define the way input and output are handled. The standard output will be redirected to “/tmp/nohup.log” file (with a “&gt; /tmp/nohup.log” and the standard error will be redirected to stdout (with the “2&gt;&amp;1” file descriptor), thus it will also go to this file which will contain both standard output and error messages from the Netcat server launching. Closing input avoids the process to read anything from standard input (waiting to be brought in the foreground to get this data). On Linux, running a job with “nohup” automatically closes its input, so we may consider not to handle this part, but finally the command I decided to use was this one.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">start_nc()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"Pause script for Control VM..." "N"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span><b>nohup nc -l 3333 &lt;/dev/null &gt;/tmp/nohup.log 2&gt;&amp;1 &amp;</b></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span><span lang="EN-US"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">The “start_nc()” function is the last one called in the “config-node.sh” file, which is executed on each cluster node. On the other side, the “ansible-config.sh” file launched in Ansible controller executes without any pause and launches the Ansible playbook. This is the role of this playbook to wait for the availability of the Netcat Server (that shows that the “config-node.sh” script is terminated with success). This is easily done with the simple task we’ve already seen. This task is executed for any host belonging to the Ansible “cluster” group, i.e all the Kubernetes cluster nodes.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">- hosts: cluster</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>become: yes</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>gather_facts: false</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>tasks:</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>- name: "Wait for port 3333 to be ready"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%"><span>&nbsp;&nbsp;&nbsp; </span>local_action: wait_for port=3333 host="{{ inventory_hostname }}" state=started connect_timeout=2 timeout=600</span><span lang="EN-US"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">One last important point is how we handle the monitoring of the automated tasks.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Monitoring the automated configuration</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Monitoring this deployment part supposes to be able to monitor both custom script extension and Ansible playbook execution. </font></font></font></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Custom script logs</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">One way to monitor both script extension and Ansible automated configuration is to connect to the provisioned VM and check the “stdout” and the “errout” files in the waagent (Azure Microsoft Azure Linux Agent that manages Linux VM interaction with the Azure Fabric Controller) directory :&nbsp; “/var/lib/waagent/Microsoft.OSTCExtensions.CustomScriptForLinux-1.4.1.0/download/0”</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image753.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb582.png" width="1114" height="446"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">A more pleasant way is to monitor the custom script execution directly from Slack.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image754.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb583.png" width="1102" height="638"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">In order to get in realtime the output from the “ansible-config.sh” and “node-config.sh” deployment custom script, you just have to define “Incoming WebHooks” in your Slack team project.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image755.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb584.png" width="1081" height="820"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">This gives you </span><span lang="EN">a WebHook extension URL which can directly be called from those bash scripts in order to s</span><span lang="EN-US">end your JSON payloads to this URL.</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">LOG_URL=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"><a href="https://hooks.slack.com/services/xxxxxxxx/yyyyyyy/zzzzzzzzzzzzzzzzzzzzzz"><u><font color="#0563c1">https://hooks.slack.com/services/xxxxxxxx/yyyyyyy/zzzzzzzzzzzzzzzzzzzzzz</font></u></a></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Then it’s quite easy to integrate this in a log function such as this one:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">log()</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span><span lang="EN-US">#...</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US"><span>&nbsp; </span></span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">mess=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$(date) - $(hostname): $1 $x"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>payload=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"payload={\"icon_emoji\":\":cloud:\",\"text\":\"$mess\"}"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>curl -s -X POST --data-urlencode </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$payload" "$LOG_URL" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">&gt; /dev/null 2&gt;&amp;1</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp;&nbsp;&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>echo "$(date) : $1"</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span><span lang="EN"></span></p> <h3 style="margin: 2pt 0cm 0pt"><span lang="EN-US"><font color="#1f4d78"><font face="Calibri Light">Ansible logs</font></font></span></h3> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font color="#000000"><font size="3"><font face="Calibri"><span lang="EN-US">Ansible logs may also be read from </span><span lang="EN">playbook execution output which is piped to a tee command in order to write it to the terminal, and to the /tmp/deploy-"${LOG_DATE}".log file.</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image756.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb585.png" width="1119" height="536"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font color="#000000"><font size="3"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">Once again it’s possible to use Slack to get Ansible logs in realtime. In order to do that we have used an existing Slack Ansible plugin (</font><a href="https://github.com/traveloka/slack-ansible-plugin"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/traveloka/slack-ansible-plugin</font></u></a><font color="#000000" size="3" face="Calibri">) which I added as submodule of our GitHub solution (</font></span><span lang="EN"><a href="https://github.com/stephgou/slack-ansible-plugin"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/stephgou/slack-ansible-plugin</font></u></a><font size="3"><font color="#000000"><font face="Calibri">). </font></font></font></span><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">Once it’s done, we just have to </span><span lang="EN">get the Slack incoming WebHook token in order to set the SLACK_TOKEN </span><span lang="EN">and SLACK_CHANNEL.</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN"><em>Please note that for practical reasons, I used base64 encoding in order to avoid to handle vm extension “fileuris” parameters outside of Github, because Github forbids token archiving. This current implement choice is definitely not a best practice. An alternative could be to put a file in a vault or a storage account and copy this file from the config-ansible.sh. </em></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN">The corresponding code is extracted from </span><span lang="EN">the following function:</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN" style="font-size: 9.5pt;font-family: consolas;color: black"></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #0070c0">function </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: #7030a0">ansible_slack_notification()</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">{</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>encoded=$ENCODED_SLACK</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>token=$(base64 -d -i &lt;&lt;&lt;</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$encoded"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">)</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>export SLACK_TOKEN=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$token"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>export SLACK_CHANNEL=</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"ansible"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>mkdir -p </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"/usr/share/ansible_plugins/callback_plugins"</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cd </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$CWD" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black">|| error_log </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"unable to back with cd .."</span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cd </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: red">"$local_kub8/$slack_repo" </span><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>pip install -r requirements.txt</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black"><span>&nbsp; </span>cp slack-logger.py /usr/share/ansible_plugins/callback_plugins/slack-logger.py</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US" style="font-size: 9.5pt;font-family: consolas;color: black;line-height: 107%">}</span><span lang="EN"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Thanks to this plugin it’s now possible to directly get Ansible logs in Slack.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image757.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb586.png" width="1113" height="770"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Conclusion</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">The combination of ARM templates, custom script extension files and Ansible playbooks </span><span lang="EN">enabled us to install the required system packages to setup and configure Ansible, to deploy required Python modules (for example in order to share SSH key files through Azure Storage), to synchronize our nodes configuration and execute Ansible playbooks to finalize the Kubernetes deployment configuration. </span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">Next time we’ll see how to automate the Kubernetes UI dashboard configuration… Stay tuned !</font></span><span lang="EN"></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Provisioning the Azure Kubernetes infrastructure with a declarative template</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/provisioning-the-azure-kubernetes-infrastructure-with-a-declarative-template</link>
      <pubDate>Fri, 19 Aug 2016 11:02:53 GMT</pubDate>
      <dc:creator><![CDATA[Stéphane GOUDEAU]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/?p=1455</guid>
      <description><![CDATA[This article is the third one about a solution we have built with my friend Hervé Leclerc (Alter Way...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">This article is the third one about a solution we have built with my friend </font></span><a href="https://twitter.com/hleclerc"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Hervé Leclerc</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> (</font></span><a href="https://www.alterway.fr/"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Alter Way</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> CTO) in order to automate a </font></span><a href="https://github.com/DXFrance/AzureKubernetes"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Kubernetes Cluster deployment on Azure</font></u></span></a><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">:</font></font></font></span><font color="#000000"><span lang="EN" style="font-family: symbol"><span><font size="3"></p> <p class="MsoNormalCxSpFirst" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Calibri"> </font></span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/11/kubernetes-cluster-automated-deployment-on-azure-first-step"><span lang="EN"><u>Kubernetes and Microsoft Azure</u></span></a></span></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/15/kubernetes-cluster-automated-deployment-on-azure-programmatic-scripting"><span lang="EN"><u>Programmatic scripting of Kubernetes deployment</u></span></a></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/19/provisioning-the-azure-kubernetes-infrastructure-with-a-declarative-template/"><span style="color: #0563c1"><u><font size="3">Provisioning the Azure Kubernetes infrastructure with a declarative template</font></u></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/27/configuring-the-azure-kubernetes-infrastructure-with-bash-scripts-and-ansible-tasks/"><span style="text-decoration: none;color: #337ab7"><font size="3">Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks</font></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Automating the Kubernetes UI dashboard configuration</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Using Kubernetes…</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US"></span></font>&nbsp;</p></font></span></span></font> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">So first let’s explain the way we have automated Kubernetes Azure provisioning through ARM Templates.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Overview of the solution</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">Kubernetes infrastructure is made up of several components – virtual machine, storage account, and virtual network which are the related and interdependent parts of a single entity. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">The targeted topology (generated with </font></span><a href="https://armviz.io/"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://armviz.io</font></u></span></a><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">) should be the following one: </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image494.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb390.png" width="1082" height="442"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Azure Resource Manager enables to deploy, manage these resources in a single, coordinated operation and monitor them as a group rather than handling them individually. With Azure Resource Manager, it’s possible to define dependencies between these resources so they are deployed in the correct order. Moreover, Azure Resource Manager provides tagging features to help manage these resources after deployment, auditing and security: Role-Based Access Control (RBAC) is natively integrated into the management platform.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461883"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Azure Resource Manager Template</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">Azure Resource Manager offers the ability to build templates and easily leverage them to deploy and manage resources on Azure. It’s a very efficient way to deploy a cluster of several virtual machines.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font face="Calibri"><font size="3"><font color="#000000"><span lang="EN">An Azure Resource Manager Template enables to deploy and manage these resources together by using a JSON description of the resources and associated deployment parameters. </span><span lang="EN-US">The template consists of JSON and expressions which you can use to construct values for your deployment. The template is organized in three sections:</span><span lang="EN"></span></font></font></font></p> <p class="MsoListParagraphCxSpFirst" style="margin: 0cm 0cm 0pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font face="Calibri"><font size="3">In the <b>parameters</b> section of the template, you specify which values you can input when deploying the resources. These parameter values enable you to customize the deployment by providing values that are tailored for your particular environment.</font></font></span></font></p> <p class="MsoListParagraphCxSpMiddle" style="margin: 0cm 0cm 0pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font face="Calibri"><font size="3">In the <b>variables</b> section, you construct values that can be used throughout your template. Typically, these variables will be based on values provided from the parameters. The objective is to simplify your template by reducing complex expressions.</font></font></span></font></p> <p class="MsoListParagraphCxSpLast" style="margin: 0cm 0cm 8pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font size="3"><font face="Calibri">In the <b>resources</b> section, you define the resources to deploy or update using types defined in resource providers. When you deploy a template, you must specify an API version to use for creating each resource. The API version corresponds to a version of REST API operations that are released by the resource provider. As a resource provider enables new features, it will release a new version of the REST API. Therefore, the version of the API you specify in your template affects which properties you can specify in the template. In general, you will want to select the most recent API version when creating new templates. For existing templates, you can decide whether you want to continue using an earlier API version, or update your template for the latest version to take advantage of new features.</font></font></span></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">For example, the following extract corresponds to the provisioning of the network resource.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image506.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb402.png" width="592" height="652"></a><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Linked ARM template</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">From within one Azure Resource Manager template, you can link to another template which enables you to decompose your deployment into a set of targeted, purpose-specific templates. This facilitates the reading and maintenance of the template. <span>&nbsp;</span></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">In our solution, provisioning of a Kubernetes cluster built upon a configurable number of etcd nodes and Kubernetes nodes (master and minions) is implemented with a linked Azure Resource Manager Template “</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">azuredeploy.json</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri">” combined with its parameters file “</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.parameters.json"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">azuredeploy.parameters.json</font></u></span></a><font size="3"><font face="Calibri"><span class="MsoHyperlink"><span lang="EN"><u><font color="#0563c1">”</font></u></span></span><span lang="EN"><font color="#000000">. </font></span></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Note : In the current armviz.io version, the previously displayed diagram is the one related to the global “azuredeploy.json” template [</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/Kubernetes-Ansible-Centos-Azure/azuredeploy.json"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/DXFrance/AzureKubernetes/blob/master/Kubernetes-Ansible-Centos-Azure/azuredeploy.json</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri">], not the linked one, which will be the one presented in this article [</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Templates/VM-Cluster/azuredeploy.json</font></u></span></a><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">].</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">The declaration for the usage of a linked template is similar to the declaration of a native Azure resource:<a href="https://msdnshared.blob.core.windows.net/media/2016/08/image507.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb403.png" width="637" height="388"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"></font></font></font></span>&nbsp;</p><span lang="EN-US"> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">The Resource Manager service must be able to access the linked templates, so we have simply used GitHub as a repository for our linked templates : <a href="https://raw.githubusercontent.com/DXFrance/AzureKubernetes/master/AzureKubernetes/Templates/VM-Cluster">https://raw.githubusercontent.com/DXFrance/AzureKubernetes/master/AzureKubernetes/Templates/VM-Cluster</a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"></span><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">The main template “<i>azuredeploy.json</i>” integrates seven resources which are also json templates. Three of these resources are shared by all the nodes and are related to the provisioning of availability sets and storage (“<i>SharedAvailabilitySetAndStorage</i>”), network (“<i>SharedNetwork</i>”, “<i>SharedPIPandLoadBalancer</i>”). The network interfaces, the virtual machines and their extension are specifically deployed and configured by type of cluster nodes (“<i>EtcdNodes</i>”,”<i>KubeMasterNodes</i>”, “<i>KubeMinionNodes</i>”, “<i>ConfigAnsibleController</i>”).</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461884"><span lang="EN-US"><font color="#2e74b5" size="4" face="Calibri Light">ARM template extension</font></span></a><span lang="EN-US"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Configuration is requested from the ARM Template and this is done through the provisioning of an extension. For example, the following is an extract from the “<i>KubeMasterNodes.json</i>” resource template.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image508.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb404.png" width="968" height="650"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">It uses the <b>CustomScriptForLinux</b> extension in order to configure the master nodes with a bash script “<i>config-node.sh</i>” which will also be executed on the other minion or etcd nodes. I will present more precisely how this script is implemented in my next article </span><span lang="EN">Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks.</span></font></font></font></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Implementing the template</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">A good JSON editor, such as Visual Studio Code can simplify the task of implementing templates, but the more efficient one would certainly be Visual Studio which brings several complementary features. First, it enables the developer to have a better view of his parameters, variables and resources by presenting the different sections of the template and enabling the navigation between the elements declared in these sections. Moreover, it includes a wizard that offers the ability to directly add resource to the current template, which enables you to generate the json script on the fly.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image498.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb394.png" width="833" height="632"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Another useful tool is Azure Resource Explorer, a site where you can discover the Azure Resource Management APIs, get API documentation and make actual API calls directly in your own subscription. <span>&nbsp;</span>It’s all done directly at the JSON level, so it always stays very close to the wire, and you can control and see everything that it’s doing.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image499.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb395.png" width="1020" height="553"></a></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Launching and monitoring the infrastructure deployment</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">The template may be directly deployed from within Visual Studio 2015 with a simple click…</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"></font></font></font></span><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image500.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb396.png" width="973" height="531"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">You can monitor the provision deployment logs in the output of Visual Studio.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image501.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb397.png" width="989" height="528"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Another way to deploy it and monitor the deployment is using a PowerShell or azure-cli scripts. Here is the P<span lang="EN">owerShell script: </span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Scripts/Deploy-VM-Cluster-PowerShell.ps1"><span lang="EN"><u><font color="#0563c1">AzureKubernetes/Scripts/</font></u></span><span lang="EN" style="text-decoration: none;color: windowtext"> </span><span lang="EN"><u><font color="#0563c1">Deploy-Kubernetes-Ansible-Centos-Azure.ps1</font></u></span></a> that may be used for this purpose.</p></font></font></font></span> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image502.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb398.png" width="981" height="691"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">You may also provision the infrastructure by directly editing the template from the portal.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image503.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb399.png" width="1053" height="566"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font color="#000000"><font face="Calibri"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri"><span lang="EN-US">And of course, independently of the way you deploy the template you may monitor the deployment directly from the portal. </span><span lang="EN">And that’s it. The following screen capture displays the result of the deployment of Kubernetes cluster resources through the linked template. It takes about 30 minutes to have the solution fully deployed.</span></p></font></font></font></span> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/08/image546.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/08/image_thumb438.png" width="1080" height="517"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Next…</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">Provisioning of the whole topology is not so difficult when you get used to it. What’s may be more complex is how to automate the configuration through extension, especially when there are dependencies between them and that’s what I will explain in my next article.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">Stay tuned!</font></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes Cluster automated deployment on Azure – Programmatic scripting</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/kubernetes-cluster-automated-deployment-on-azure-programmatic-scripting</link>
      <pubDate>Fri, 15 Jul 2016 08:27:34 GMT</pubDate>
      <dc:creator><![CDATA[Stéphane GOUDEAU]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/?p=1205</guid>
      <description><![CDATA[This article is the second one about a solution we have built with my friend Hervé Leclerc (Alter...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">This article is the second one about a solution we have built with my friend </font></span><a href="https://twitter.com/hleclerc"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Hervé Leclerc</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> (</font></span><a href="https://www.alterway.fr/"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Alter Way</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> CTO) in order to automate a </font></span><a href="https://github.com/DXFrance/AzureKubernetes"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Kubernetes Cluster deployment on Azure</font></u></span></a><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">:</font></font></font></span></p><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333"> <p class="MsoNormalCxSpFirst" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Calibri"> </font></span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/11/kubernetes-cluster-automated-deployment-on-azure-first-step"><span lang="EN"><u>Kubernetes and Microsoft Azure</u></span></a></span></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/15/kubernetes-cluster-automated-deployment-on-azure-programmatic-scripting"><span lang="EN"><u>Programmatic scripting of Kubernetes deployment</u></span></a></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/19/provisioning-the-azure-kubernetes-infrastructure-with-a-declarative-template/"><span style="color: #0563c1"><u><font size="3">Provisioning the Azure Kubernetes infrastructure with a declarative template</font></u></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/27/configuring-the-azure-kubernetes-infrastructure-with-bash-scripts-and-ansible-tasks/"><span style="text-decoration: none;color: #337ab7"><font size="3">Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks</font></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Automating the Kubernetes UI dashboard configuration</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Using Kubernetes…</font></span></font></p></span></font><font size="3" face="Calibri"><span lang="EN-US"></span></font> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"></font></font></font></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">So let’s explain different ways of automating Kubernetes deployment through scripting technologies (Azure CLI and PowerShell).</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461878"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Overview of the solution</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Provisioning of a Kubernetes cluster built upon a configurable number of etcd nodes and Kubernetes nodes (master and minions) is implemented with Azure CLI (</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/init-static/init-script.sh"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">init-static/init-script.sh</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri">) and PowerShell (</font></span><a href="https://github.com/DXFrance/AzureKubernetes/blob/master/AzureKubernetes/Scripts/Deploy-VM-Cluster-PowerShell.ps1"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">AzureKubernetes/Scripts/Deploy-VM-Cluster-PowerShell.ps1</font></u></span></a><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">). </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">Configuration of cluster CoreOS nodes is achieved through declaration in <b>cloud-config file</b> of various OS-level items (network configuration, user accounts, systemd units) that will be used by <b>coreos-cloudinit</b> program to configures the OS after startup or during runtime (</font></span><a href="https://coreos.com/os/docs/latest/cloud-config.html"><span lang="EN-US"><u><font color="#0563c1" size="3" face="Calibri">https://coreos.com/os/docs/latest/cloud-config.html</font></u></span></a><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">). </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">The whole deployment is achieved within about 20 minutes.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461879"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Cloud-config file</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">The implemented Azure CLI generates a cloud-config files depending on the configuration. The PowerShell script implementation goes a bit less far. It simply reuses yml files generated by Azure CLI (“init-static\custom-data\kubernetes-cluster-etcd-nodes.yml” for etc nodes and “init-static\custom-data\kubernetes-cluster-main-nodes-template.yml" for master and minion nodes) in order to apply the targeted nodes configuration. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font face="Calibri"><font size="3"><font color="#000000">For both cloud-config files (“kubernetes-cluster-etcd-nodes.yml” and “kubernetes-cluster-main-nodes-template.yml”), the file uses the <b>YAML file format</b>, which uses whitespace and new-lines to delimit lists, associative arrays, and values. It contains a “#cloud-config” header followed by an associative array “coreos” and “write_files” keys:</font></font></font></span></p> <p class="MsoListParagraphCxSpFirst" style="margin: 0cm 0cm 0pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font face="Calibri"><font size="3">The coreos.update.* parameters manipulate settings related to how CoreOS instances are updated. These fields will be written out to and replace “/etc/coreos/update.conf”. The “coreos.units.*” parameters define a list of arbitrary systemd units to start after booting. This feature is intended to start essential services required to mount storage and configure networking. Each item is an object with the following fields: name (unit’s name), content (unit file), command (command to execute on unit: start, stop, reload, restart,...).</font></font></span></font></p> <p class="MsoListParagraphCxSpLast" style="margin: 0cm 0cm 8pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font size="3"><font face="Calibri">The write_files directive defines a set of files to create on the local filesystem. Each item in the list may have the following keys: path (absolute location on disk where contents should be written, content (data to write at the provided path), permissions, owner….</font></font></span></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span lang="EN">The following “</span><span lang="EN-US">kubernetes-cluster-etcd-nodes.yml”</span><span lang="EN-US"> </span><span lang="EN">file is the cloud-config file for the etcd node. It’s a very short one, because the etcd role is quite simple to configure.</span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image465.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/07/image_thumb412.png" width="1124" height="974"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span lang="EN"></span></font></font></font>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font face="Calibri"><font color="#000000"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image470.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;float: right;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/07/image_thumb417.png" width="404" align="right" height="401"></a>The “kubernetes-cluster-main-nodes-template.yml” file is quite bigger. It declares for each role, the different required services. For example, in the master node, the unit name “kubernetes-master” requires “weave network” and will be configured with “kube-apiserver”, “kube-scheduler”, “kube-controller-manager” and “kube-proxy” services, as described on Kubernetes cluster deployment topology.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt">&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font face="Calibri"><font color="#000000">The launch of the corresponding service will be done by bash script inside “ExecStartPre” or “ExecStart” commands. For example, the installation of the “kube-scheduler” service is the following: </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image467.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/07/image_thumb414.png" width="1146" height="464"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font size="3"><font face="Calibri"><font color="#000000">These services may themselves be dependent on other ones. For example, all Kubernetes services require first the installation of Kubernetes, which is done in the following way: </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image468.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/07/image_thumb415.png" width="1149" height="699"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span></span><span lang="EN-US"></span></font></font></font></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font face="Calibri"><font color="#000000">Once the cloud-config files are ready, you just have to inject them with the “CustomData” option which is available for both Azure CLI and PowerShell. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><font size="3"><font face="Calibri"><font color="#000000"><span lang="EN-US">In PowerShell, </span><span lang="EN">the corresponding parameter is “-CustomData”:</span></font></font></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt;line-height: normal"><span lang="EN"><span><font color="#000000">&nbsp;</font></span></span><span lang="EN-US"><span><font color="#000000">&nbsp;&nbsp;&nbsp; </font></span><span style="color: orangered">$vm</span><font color="#000000"> </font><span style="color: darkgray">=</span><font color="#000000"> </font><span style="color: blue">Set-AzureRmVMOperatingSystem</span><font color="#000000"> </font><span style="color: navy">-VM</span><font color="#000000"> </font><span style="color: orangered">$vm</span><font color="#000000"> </font><span style="color: navy">-Linux</span><font color="#000000"> </font><span style="color: navy">-ComputerName</span><font color="#000000"> </font><span style="color: orangered">$vmName</span><font color="#000000"> `</font></span></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 35.4pt;line-height: normal;text-indent: 35.4pt"><span lang="EN-US">-Credential</span><span lang="EN-US"><font color="#000000"> </font><span style="color: orangered">$credential</span><font color="#000000"> </font><span style="color: navy">-CustomData</span><font color="#000000"> </font><span style="color: orangered">$customData</span><font color="#000000"><span>&nbsp;&nbsp;&nbsp; </span></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt">&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">In Azure CLI, the corresponding parameter is “--custom-data”:</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image471.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/07/image_thumb418.png" width="829" height="432"></a></font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">And that’s it for the programmatic deployment of a Kubernetes cluster.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Stay tune for the next article. We will speak about a declarative way to reach the same objective.</font></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes Cluster automated deployment on Azure – First Step</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/kubernetes-cluster-automated-deployment-on-azure-first-step</link>
      <pubDate>Mon, 11 Jul 2016 12:11:44 GMT</pubDate>
      <dc:creator><![CDATA[Stéphane GOUDEAU]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/?p=1075</guid>
      <description><![CDATA[Last week was published a video which I thing may be usefully completed by a few articles that gives...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Last week was published a </font></span><a href="https://www.alterway.fr/deployer-un-cluster-kubernetes-sur-azure-en-utilisant-les-templates-arm-et-loutil-devops-ansible"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">video</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> which I thing may be usefully completed by a few articles that gives more details … This video was about a solution we have built with my friend <a href="https://twitter.com/hleclerc"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Hervé Leclerc</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> (</font></span></font></span><a href="https://www.alterway.fr/"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">Alter Way</font></u></span></a><span lang="EN"><font color="#000000" size="3" face="Calibri"> CTO) in order to automate a Kubernetes Cluster deployment on Azure. This was the opportunity for Hervé</font></span><span lang="EN"><font color="#000000" size="3" face="Calibri"> and I to give an overview of this Open Source </font></span><a href="https://github.com/DXFrance/AzureKubernetes"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">implementation</font></u></span></a><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font face="Calibri"><font size="3"><font color="#000000">These are the articles I have planned to write:</font></font></font></span></p><font color="#000000"><span lang="EN" style="font-family: symbol"><span><font size="3"><span lang="EN-US" style="font-size: 10.5pt;font-family: symbol;color: #333333"> <p class="MsoNormalCxSpFirst" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Calibri"> </font></span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/11/kubernetes-cluster-automated-deployment-on-azure-first-step"><span lang="EN"><u>Kubernetes and Microsoft Azure</u></span></a></span></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/07/15/kubernetes-cluster-automated-deployment-on-azure-programmatic-scripting"><span lang="EN"><u>Programmatic scripting of Kubernetes deployment</u></span></a></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: #333333">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/19/provisioning-the-azure-kubernetes-infrastructure-with-a-declarative-template/"><span style="color: #0563c1"><u><font size="3">Provisioning the Azure Kubernetes infrastructure with a declarative template</font></u></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormalCxSpMiddle" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN"><a href="https://blogs.msdn.microsoft.com/stephgou/2016/08/27/configuring-the-azure-kubernetes-infrastructure-with-bash-scripts-and-ansible-tasks/"><span style="text-decoration: none;color: #337ab7"><font size="3">Configuring the Azure Kubernetes infrastructure with Bash scripts and Ansible tasks</font></span></a></span><span lang="EN-US"></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Automating the Kubernetes UI dashboard configuration</font></span></font></p> <p class="MsoNormal" style="background: white;margin: 0cm 0cm 0pt 36pt;line-height: normal;text-indent: -18pt"><font face="Calibri"><span lang="EN-US" style="font-size: 10pt;font-family: symbol;color: black">·</span><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span lang="EN-US"><font size="3">Using Kubernetes…</font></span></font></p></span> <p class="MsoListParagraphCxSpFirst" style="margin: 0cm 0cm 0pt 36pt;text-indent: -18pt"></font></span></span></font>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Let’s start first by presenting Kubernetes and why and how to deploy it in Azure.</font></span><span lang="EN"></span></p> <h2 style="margin: 2pt 0cm 0pt"><span lang="EN"><font size="4"><font color="#2e74b5"><font face="Calibri Light">Kubernetes</font></font></font></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Kubernetes is an open-source orchestration platform for automating deployment, operations, and scaling of applications across multiple hosts. It targets applications composed of multiple Docker containers, such as distributed micro-services and provides ways for containers to find and communicate with each other. It enables users to ask a cluster to run a set of containers with an automatic scheduling and choice of the hosts to run those containers on. It regroups containers that make up an application into logical units for easy management with self-healing mechanisms (auto-restarting, re-scheduling, and replicating containers) and discovery. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Kubernetes has been created by Google but contributors to this project now include IBM, Microsoft, Red Hat, CoreOS, Mesosphere, and others… </font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461870"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Kubernetes concepts</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">A Kubernetes cluster is made up of several nodes with different roles such as <b>master</b>, <b>minions</b>, and <b>etcd</b> (</font></span><a href="https://github.com/kubernetes/kubernetes#concepts"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/kubernetes/kubernetes#concepts</font></u></span></a><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">). The master is responsible for managing the cluster and issuing requests to be performed by the minions. The minions are the Kubernetes nodes that actually run Docker workloads. <b>Pods</b> represent a co-located group of application containers with shared volumes. They are scheduled to run and are balanced across the minions by the master. <b>Replication controllers</b> manage the lifecycle of pods and ensure that a specified number of pods are running at any given time, while <b>services</b> provide a single, stable name and address for a set of pods. Etcd is a distributed, highly-available, consistent key-value store for shared configuration and service discovery which Kubernetes uses for persistent storage of all of its REST API objects.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461871"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Kubernetes networking model</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font face="Calibri"><font size="3"><font color="#000000">For communicating over a network, containers are tied to the IP addresses of the host machines and must rely on port-mapping to reach the desired container. This makes it difficult for applications running inside containers to advertise their external IP and port as that information is not available to them. Kubernetes allocates an IP address to each pod. It is required to allocate a different block of IPs to each node in the cluster as the node is added, so any process in one pod could communicate with another one running in a second pod through IP connectivity. </font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font face="Calibri"><font size="3"><font color="#000000">This can be achieved by configuring a network to route Pod IPs or by creating an <b>overlay network with weave or flannel</b>:</font></font></font></span></p> <p class="MsoListParagraphCxSpFirst" style="margin: 0cm 0cm 0pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font face="Calibri"><font size="3">A weave network consists of a number of “peers” - weave routers residing on different hosts. Weave routers establish TCP connections to each other, over which they perform a protocol handshake and subsequently exchange topology information. </font></font></span></font></p> <p class="MsoListParagraphCxSpLast" style="margin: 0cm 0cm 8pt 36pt;text-indent: -18pt"><font color="#000000"><span lang="EN-US" style="font-family: symbol"><span><font size="3">·</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US"><font size="3"><font face="Calibri">A flannel network gives each container an IP that can be used for container-to-container communication. It uses packet encapsulation to create a virtual overlay network that spans the cluster. It gives each host an IP subnet from which the Docker daemon is able to allocate IPs to the individual containers. flannel uses etcd to store mappings between the virtual IP and host addresses. </font></font></span></font></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461872"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Kubernetes cluster deployment topology</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Depending on networking model (weave or flannel), Kubernetes cluster deployment topology should correspond to the following representation.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/image910.png"><img title="image[9]" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image[9]" src="https://msdnshared.blob.core.windows.net/media/2016/07/image9_thumb.png" width="1132" height="670"></a></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><a name="_Toc452461874"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Why Kubernetes on Microsoft Azure?</font></span></a><span lang="EN"></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">Docker brings a more flexible deployment model using containers as its runtime. Kubernetes offers a way to manage those containers that imposes less overhead on servers when multiple workloads are shared on a single machine. Moreover, containers can be fired up and removed at a much faster pace than with machine virtualization. Kubernetes is intended to run on physical hosts as well as cloud providers such as Microsoft Azure. That’s why Scott Guthrie, Executive Vice President of the Cloud and Enterprise group at Microsoft has stated: “Microsoft will help contribute code to Kubernetes to enable customers to easily manage containers that can run anywhere. This will make it easier to build multi-cloud solutions including targeting Microsoft Azure.”</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461875"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Running Kubernetes on Azure</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN-US"><font color="#000000" size="3" face="Calibri">Kubernetes documentation offers detailed guidance on the way to deploy a Kubernetes cluster to Azure (</font></span><a href="https://kubernetes.io/docs/getting-started-guides/coreos/azure"><span lang="EN-US"><u><font color="#0563c1" size="3" face="Calibri">https://kubernetes.io/docs/getting-started-guides/coreos/azure</font></u></span></a><span lang="EN-US"><font color="#000000" size="3" face="Calibri">). The proposed implementation relies on the usage of CoreOs virtual machines, with Weave Network for communication. Provisioning of Kubernetes master, minions and etcd nodes is done through Azure CLI and call to a node.js script “./create-kubernetes-cluster.js”, which is published on </font></span><a href="https://github.com/kubernetes/kubernetes"><span lang="EN-US"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/kubernetes/kubernetes</font></u></span></a><span lang="EN-US"><font size="3"><font color="#000000"><font face="Calibri">. This script provisions a cluster with a ring of three dedicated etcd nodes, one kubernetes master and two kubernetes minions for the containers workloads deployment.</font></font></font></span></p> <h2 style="margin: 2pt 0cm 0pt"><a name="_Toc452461876"><span lang="EN"><font color="#2e74b5" size="4" face="Calibri Light">Automating a Kubernetes cluster deployment in Azure</font></span></a><span lang="EN"></span></h2> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">Automating a Kubernetes cluster deployment in Azure requires first cluster provisioning, and then each cluster node configuration. Scripted cluster provisioning in Azure can be done in many ways, that’s why we propose three different solutions: PowerShell ARM cmdlets, Azure CLI and json ARM Templates. Configuration requires the ability to extend the configuration of the different nodes. This can be done through cloud-config file CoreOs mechanism. It can also be achieved through the usage of Ansible tasks. We have also explored and implemented both ways.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000"><font size="3"><font face="Calibri">So first solution is to provision a Kubernetes through PowerShell or Azure CLI using cloud-config file. Second solution is an ARM json template built upon linked templates, that relies on Ansible tasks for Kubernetes roles setup.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">All the following code is published online on </font></span><a href="https://github.com/DXFrance/AzureKubernetes"><span lang="EN"><u><font color="#0563c1" size="3" face="Calibri">https://github.com/DXFrance/AzureKubernetes</font></u></span></a><span lang="EN"><font size="3"><font color="#000000"><font face="Calibri">.</font></font></font></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 8pt"><span lang="EN"><font color="#000000" size="3" face="Calibri">Stay tuned for the next article…</font></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Storage and Analysis Platform Matrix</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/storage-and-analysis-platform-matrix</link>
      <pubDate>Fri, 08 Jul 2016 21:06:30 GMT</pubDate>
      <dc:creator><![CDATA[Stéphane GOUDEAU]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/?p=1065</guid>
      <description><![CDATA[It’s been a long time since I haven’t blogged. In the meanwhile, I was quite busy working on...]]></description>
      <content:encoded><![CDATA[<p><font size="3">It’s been a long time since I haven’t blogged. In the meanwhile, I was quite busy working on different customer projects and writing ”<a href="https://livre.fnac.com/a9377738/Stephane-Goudeau-Decouvrir-DevOps">Découvrir DevOps</a>”. As said Patrick Debois about it, "This book brings devops from multiple viewpoints: everyone participating in the delivery chain has their view on reality. Reframing mutual problems through the lens of others help you gain the understanding and a more global view on the problem space. The journey from decision to delivery in production is taken as a guiding story reflecting the usual inner devops journey in companies. At the same time the book balances between technology and process to show the symbiotic relation exists between them. Both don't exist in a vacuum and will influence eachother. Bringing down technical and organizational silos are crucial to the change of mindset. Time to read!"</font></p> <p><font size="3">But today, I will not blog about DevOps. This article is the extension of my previous <a href="https://blogs.msdn.microsoft.com/stephgou/2015/08/28/microsoft-cloud-data-platform">Microsoft Cloud Data Platform </a>post. I would like to share a comparison matrix built with my friend <a href="https://twitter.com/benjguin">Benjamin Guinebertière</a> on the multiple ways of envisioning a Storage and Analysis Platform with Microsoft Azure Services, depending on several criteria : programming model, batch vs realtime, provisioning duration, pricing, reversibility, scaling, stop/start option, proprietary vs Open Source…</font></p> <p><font size="3"><a href="https://msdnshared.blob.core.windows.net/media/2016/07/data-matrix1.png"><img title="data-matrix" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="data-matrix" src="https://msdnshared.blob.core.windows.net/media/2016/07/data-matrix_thumb.png" width="1025" height="484"></a></font></p> <p><font size="3">I hope you will find this matrix useful. Please do not hesitate to comment this attempt of comparison so I could update and enhance this table.<br></font></p>]]></content:encoded>
    </item>
    <item>
      <title>Implémentation d’une solution fondée sur les Azure App Services : Code et configuration de l’application</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/implmentation-dune-solution-fonde-sur-les-azure-app-services-code-et-configuration-de-lapplication</link>
      <pubDate>Mon, 28 Sep 2015 08:53:40 GMT</pubDate>
      <dc:creator><![CDATA[stephgou]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/2015/09/28/implmentation-dune-solution-fonde-sur-les-azure-app-services-code-et-configuration-de-lapplication/</guid>
      <description><![CDATA[Voici donc le septième et dernier d’une série d’articles consacrée à l'implémentation d’une solution...]]></description>
      <content:encoded><![CDATA[<p>Voici donc le septième et dernier d’une série d’articles consacrée à l'implémentation d’une solution fondée sur les Azure App Services organisée de la façon suivante :</p>  <ul>   <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/17/implementation-d-une-solution-fondee-sur-les-azure-app-services.aspx">Solution et choix d’implémentation</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx">Mise en œuvre d’un service de recherche</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-web.aspx">Consommation du service de recherche par une application Web</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-exposition-du-service-de-recherche.aspx">Exposition du service de recherche</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/12/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle.aspx">Consommation du service de recherche par une application Universelle</a> </li>    <li><a title="Gestion d’identité" href="https://blogs.msdn.com/b/stephgou/archive/2015/09/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-gestion-d-identite.aspx">Gestion d’identité</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-code-et-configuration-de-l-application.aspx">Code et configuration de l’application</a> </li> </ul>  <p>Nous voici donc au terme de notre exploration de l’implémentation d’une solution fondée sur les Azure App Services. L’objectif est maintenant de vous permettre de récupérer le code et de déployer la solution correspondante dans le Cloud.    <br /></p>  <h1>L’environnement de développement </h1>  <p>Il vous faudra installer un Visual Studio dans sa version 2015. La « <a href="https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx">Version communautaire</a> » devrait théoriquement suffire, mais si vous disposez d’une version « Professional » ou « Entreprise », c’est encore mieux…     <br />Le Microsoft Azure SDK for .NET - 2.7 Azure est disponible <a href="https://www.microsoft.com/en-us/download/details.aspx?id=48178">ici</a>. Je vous invite à plutôt utiliser Microsoft Web Platform Installer (<a href="https://www.microsoft.com/web/downloads/platform.aspx">Web PI 5.0</a>), l’outil de référence pour mettre à jour les derniers composants de la plateforme Microsoft.     <br />Un simple Build devrait vous permettre de récupérer les packages NuGet requis (à condition d’activer l’option « Enable NuGet Package Restore » par un simple clic droit sur la solution Visual Studio). </p>  <h1>Code de l’application </h1>  <p>Le code de l’application est <a href="https://github.com/stephgou/Thot">ici</a>. Vous pouvez le récupérer en le téléchargeant depuis le portail GitHub ou&#160; avec la ligne de commande : <a href="https://github.com/stephgou/Thot.git">https://github.com/stephgou/Thot.git</a></p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7002.image_2417FABF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7002.image_5F00_2417FABF.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2642.image_thumb_34B5170F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2642.image_5F00_thumb_5F00_34B5170F.png" width="520" height="277" /></a></p>  <h1>Initialisation des services Backend</h1>  <p>La première étape consiste à ouvrir une souscription Azure pour ceux qui n’en disposeraient pas encore. Pour ce faire, je vous invite à lire l’article « <a href="https://aka.ms/Azure0Euro">Testez Azure gratuitement pendant un mois</a> » de mon collègue et ami <a href="https://blogs.msdn.com/b/benjguin/">Benjamin Guinebertière</a>.     <br />Une fois votre souscription activée, il faut créer un compte et un conteneur Microsoft Azure Storage. La procédure à suivre est décrite dans l’article du MSDN « <a href="https://msdn.microsoft.com/fr-fr/library/dn466439(v=sql.120).aspx">Leçon 1 : Créer un compte et un conteneur Microsoft Azure Storage</a> » .     <br />Il faut ensuite créer une instance de service « Azure Search ». Le début du tutorial « <a href="https://azure.microsoft.com/fr-fr/documentation/articles/search-get-started-dotnet">Prise en main de votre première application Azure Search dans .NET</a> » devrait vous permettre de rapidement réaliser cette tâche…     <br />La création du tenant Azure Active Directory se fait depuis le portail actuel.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3678.image_26E561C5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3678.image_5F00_26E561C5.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1680.image_thumb_0F4DB452.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1680.image_5F00_thumb_5F00_0F4DB452.png" width="524" height="264" /></a>     <br />Enfin, il faut y déclarer au moins un utilisateur de l’application. L’article « <a href="https://azure.microsoft.com/fr-fr/documentation/articles/active-directory-create-users/">Création ou modification d’utilisateurs dans Azure AD</a> » devrait vous éclairer sur ce point.</p>  <h1>Configuration des paramètres liés aux services Backend</h1>  <p>Une fois ces premiers éléments configurés, il faut ensuite renseigner dans le code les différentes clés Azure (« Azure Storage », « Azure Search ») requises pour faire fonctionner cette solution. Les fichiers à modifier dans la solution Visual Studio (« Doc.Search ») sont, en premier lieu, les fichiers de configuration :</p>  <ul>   <li>Le fichier « App.Config » du projet « BuildIndex.Search.App » correspondant à l’application console qui permet de créer et d’initialiser l’index du service de recherche à partir du contenu d’un compte de stockage. </li>    <li>Le fichier « Web.Config » du projet « Doc.Search.Portal » correspondant à la « Web App » permettant de télécharger des documents sur le stockage Azure et d’effectuer des recherches sur ce contenu. </li>    <li>Le fichier « Web.Config » du projet « Doc.Search.API » correspondant à l’« API App » exposant de façon sécurisée le service de recherche pour l’application Universelle. </li> </ul>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><font color="#0000ff"><span style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160; </font></span></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us">&lt;</span></font></font><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">appSettings</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff">&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">SearchServiceApiKey</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">SearchServiceName</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">ApiKey</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">ApiVersion</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">2014-07-31-Preview</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">Endpoint</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">nom-endpoint-search-service.search.windows.net</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">QueryKey</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">IndexName</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">SearchCriteria</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">docx</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">AzureStorage.ConnectionString</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">DefaultEndpointsProtocol=https;AccountName=searchdoc;AccountKey=…</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 0pt; line-height: normal; text-autospace: ; mso-layout-grid-align: none"><font face="Consolas"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160;&#160;&#160; </font></span><font style="font-size: 9.5pt">&lt;</font></font></span><font style="font-size: 9.5pt"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#a31515">add</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">key</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">AzureStorage.ContainerReference</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff"> </font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#ff0000">value</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">=</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#0000ff">public</font></span><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font color="#000000">&quot;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"><font style="font-size: 9.5pt" color="#0000ff"> /&gt;</font></span></font><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; mso-highlight: white; mso-ansi-language: en-us"></span></p>  <p class="MsoNormal" style="list-style-type: disc; margin: 0cm 0cm 8pt; line-height: 12pt"><font face="Consolas"><font color="#0000ff"><span lang="EN-US" style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; line-height: 10pt; mso-highlight: white; mso-ansi-language: en-us"><span style="mso-spacerun: yes"><font style="font-size: 9.5pt">&#160; </font></span></span><font style="font-size: 9.5pt"><span style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; line-height: 10pt; mso-highlight: white">&lt;/</span></font></font><font style="font-size: 9.5pt"><span style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; line-height: 10pt; mso-highlight: white"><font color="#a31515">appSettings</font></span></font><span style="font-family: ; background-image: none; background-repeat: repeat; background-attachment: scroll; background-position: 0% 0%; color: ; line-height: 10pt; mso-highlight: white"><font style="font-size: 9.5pt" color="#0000ff">&gt;</font></span></font></p>  <p>&#160;</p>  <p>Les paramètres SearchServiceApiKey (« PRIMARY ADMIN KEY ») et QueryKey (« docs ») sont disponibles dans le portail (partie masquée en rouge sur la copie d’écran).</p> <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7485.image_1A79CF4D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7485.image_5F00_1A79CF4D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3056.image_thumb_058ADD8B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3056.image_5F00_thumb_5F00_058ADD8B.png" width="520" height="191" /></a>&#160; <br />Le paramètre AzureStorage.ConnectionString (« PRIMARY CONNECTION STRING ») est disponible dans le portail (partie encadrée en rouge sur la copie d’écran).   <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0184.image_5733E88E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0184.image_5F00_5733E88E.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6663.image_thumb_34067DDC.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6663.image_5F00_thumb_5F00_34067DDC.png" width="520" height="214" /></a>     <br />Enfin, avant de pouvoir utiliser l’application, il faut définir l’index. Pour ce faire, il suffit de lancer l’application console « BuildIndex.Search.App » qui automatise la création de cette index selon la définition décrite dans l’article <a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx">Implémentation d’une solution fondée sur les Azure App Services : Mise en œuvre du service de recherche</a>.</p>  <h1>Publication des services Backend </h1>  <p>La publication des Azure App Service est réalisée directement depuis Visual Studio.    <br />La première étape consiste à déployer la « Web App ». </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8780.image_18648297.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8780.image_5F00_18648297.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4263.image_thumb_0545E69C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4263.image_5F00_thumb_5F00_0545E69C.png" width="520" height="290" /></a>     <br />Le site devrait être opérationnel, il est indépendant de l’« API App ». Je vous invite à vérifier son bon fonctionnement pour vous assurer que les paramètres ont été bien renseignés.     <br />Puis il faut déployer l’« API App ». L’« API Gateway » sera créée par Visual Studio lors de la publication de l’API.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8420.image_503BE81C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8420.image_5F00_503BE81C.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5758.image_thumb_7B16DCDF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5758.image_5F00_thumb_5F00_7B16DCDF.png" width="520" height="295" /></a></p>  <p>Enfin, il faut configurer cette « API App » et l’« API Gateway » en suivant les indications fournies dans mon précédent article « <a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-gestion-d-identite.aspx">Implémentation d’une solution fondée sur les Azure App Services : Gestion d’identité</a> ».     <br />Je vous invite à vérifier que l’API App fonctionne avant sécurisation de l’« API Gateway », puis une fois la configuration achevée, en appliquant la démarche décrite dans ce même article.</p>  <h1>Configuration de l’application universelle </h1>  <br />  <p>Il ne reste plus qu’à renseigner les paramètres définis au sein de la classe « ThotSettings » dans le projet « Doc.Search.App » correspondant à l’application Universelle.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8037.image_513668AA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8037.image_5F00_513668AA.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1524.image_thumb_02C466F1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1524.image_5F00_thumb_5F00_02C466F1.png" width="520" height="119" /></a>     <br />Ces paramètres API_APP (« URL ») et GATEWAY (« Gateway ») sont lisibles directement sur le nouveau portail Azure, au niveau de l’« API App ». </p>  <p>&#160;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1351.image_04C77219.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1351.image_5F00_04C77219.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2821.image_thumb_3343126A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2821.image_5F00_thumb_5F00_3343126A.png" width="520" height="323" /></a></p>  <h1>Conclusion</h1>  <br />  <p>En principe, vous devriez maintenant pouvoir lancer cette application sans difficulté particulière.    <br />Je n’ai pas eu le temps de m’assurer que la mise en œuvre des différentes étapes que je viens d’énoncer sur un environnement « vierge » permettait de parvenir à remonter la solution sans encombre. Je vous invite donc à me remonter tout élément permettant d’améliorer cette procédure. </p>  <p>D’avance, merci pour votre contribution…</p>]]></content:encoded>
    </item>
    <item>
      <title>Implémentation d’une solution fondée sur les Azure App Services : Gestion d’identité</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/implmentation-dune-solution-fonde-sur-les-azure-app-services-gestion-didentit</link>
      <pubDate>Mon, 21 Sep 2015 00:41:44 GMT</pubDate>
      <dc:creator><![CDATA[stephgou]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/2015/09/21/implmentation-dune-solution-fonde-sur-les-azure-app-services-gestion-didentit/</guid>
      <description><![CDATA[Voici donc le sixième article d’une série d’articles consacrée à l'implémentation d’une solution...]]></description>
      <content:encoded><![CDATA[<p>Voici donc le sixième article d’une série d’articles consacrée à l'implémentation d’une solution fondée sur les API App Services et qui sera organisée de la façon suivante :</p>  <ul>   <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/17/implementation-d-une-solution-fondee-sur-les-azure-app-services.aspx">Solution et choix d’implémentation</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx">Mise en œuvre d’un service de recherche</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-web.aspx">Consommation du service de recherche par une application Web</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-exposition-du-service-de-recherche.aspx">Exposition du service de recherche</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/12/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle.aspx">Consommation du service de recherche par une application Universelle</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-gestion-d-identite.aspx">Gestion d’identité</a> </li>    <li><a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-code-et-configuration-de-l-application.aspx">Code et configuration de l’application</a> </li> </ul>  <p>L’objectif est maintenant de filtrer les accès sur l’« API App », exposée au-dessus du service backend « Azure Search » et d’adapter l’application Windows Universelle pour qu’elle puisse utiliser cette « API App », en donnant la possibilité à l’utilisateur de s’identifier depuis cette application…</p>  <h1>Services d’authentification « Azure App Service »</h1>  <p>« Azure App Service » propose des services d'authentification intégrés qui implémentent nativement OAuth 2.0 et OpenID et peuvent s’intégrer avec plusieurs fournisseurs d'identité (interne comme « Azure Active Directory » ou externes comme Microsoft Account, Google, Twitter ou Facebook).    <br />Dans l’implémentation proposée, l’accès à l’« API App » sera donc maintenant restreint aux utilisateurs authentifiés sur « Azure Active Directory » (AAD).</p>  <h1>« Azure Active Directory »</h1>  <p>« Azure Active Directory » est un service Cloud de gestion d'identité destiné à offrir un accès par authentification unique à des milliers d’Applications SaaS, comme par exemple « Office 365 » ou Salesforce.com, .... « Azure Active Directory » propose de nombreuses fonctions comme l’authentification multi-facteurs ou l'intégration avec « Windows Server Active Directory ». Dans le contexte de l’implémentation de cette solution, si le choix s'est porté sur ce service, c'est surtout parce qu'il permet de rapidement intégrer un mécanisme d'authentification fondé sur l'utilisation d'un annuaire facile à administrer (tout en offrant une interface de gestion de mot de passe, en libre-service pour les utilisateurs qui y sont déclarés).<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3187.image_63BB0CB8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3187.image_5F00_63BB0CB8.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4201.image_thumb_7A10B1FD.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4201.image_5F00_thumb_5F00_7A10B1FD.png" width="530" height="212" /></a></p>  <h1>Configuration de l’« API App »</h1>  <p>L’ « API App » propose trois paramètres d’authentification (« Access Level » dans les « Application Settings ») :</p>  <ul>   <li>Public (anonyme) - n'importe qui peut appeler l’« API App ». </li>    <li>Public (authentifié) - seuls les utilisateurs authentifiés sont autorisés à appeler l’« API App » en dehors du groupe de ressources dans lequel est déployée l’« API App ». </li>    <li>Interne - l’« API App » peut uniquement être appelée par d'autres « API App » déployées dans le même groupe de ressources. </li> </ul> <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4135.image_42CA2AC2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4135.image_5F00_42CA2AC2.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0336.image_thumb_0B83A387.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0336.image_5F00_thumb_5F00_0B83A387.png" width="530" height="250" /></a>   <p>Lorsque l’on souhaite configurer l’« API App » pour accepter uniquement les requêtes authentifiées, la première étape consiste donc à paramétrer son niveau d’accès sur Public (authentifié). Et pour exiger une authentification auprès d'un fournisseur d’identité comme « Active Directory Azure », il faut maintenant configurer une passerelle : l’« API Gateway ».</p>  <h1>Configuration de l’« API Gateway »</h1>  <p>L’« API Gateway » est créée par Visual Studio lors de la publication de l’API. Il s’agit d’une application web dont le rôle est de filtrer toutes les requêtes HTTP entrantes destinées aux « API Apps » du même groupe de ressources pour y inclure diverses tâches administratives, dont l'authentification.    <br />La passerelle gère les procédures de connexion, les jetons et empêche les appels non authentifiés d'atteindre l’« API App » configurée pour exiger ce type d’accès.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1778.image_1B263F49.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1778.image_5F00_1B263F49.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8640.image_thumb_7CDB8852.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8640.image_5F00_thumb_5F00_7CDB8852.png" width="530" height="263" /></a></p>  <p>Le fournisseur d’identité de notre solution est « Azure Active Directory ». Pour bénéficier de ces services, il est nécessaire d’enregistrer l’« API App » « Doc.Search.API » en tant qu’application AAD.    <br />Pour ce faire, on peut utiliser le portail Azure actuel et renseigner les paramètres « Sign-on URL », « App ID URI », et « Reply URL » obtenus par simple copie depuis le nouveau portail Azure, dans la configuration AAD de la passerelle.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1258.image_5A1A5095.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1258.image_5F00_5A1A5095.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3240.image_thumb_17AA3F10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3240.image_5F00_thumb_5F00_17AA3F10.png" width="530" height="384" /></a>On obtient alors un « Client ID » pour l’« API App » que l’on peut ensuite reporter, avec le « Allowed tenant » dans la configuration de la passerelle sur le nouveau portail.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0552.image_0E510A8D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0552.image_5F00_0E510A8D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5707.image_thumb_5294028A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5707.image_5F00_thumb_5F00_5294028A.png" width="530" height="243" /></a></p>  <h1>Evolution du code de l’« API App »</h1>  <p>La première évolution de l’« API App » consiste à modifier le fichier « apiapp.json » pour déclarer le fournisseur d’identité que l’on souhaite utiliser.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5710.image_68E9A7CF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5710.image_5F00_68E9A7CF.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5153.image_thumb_0AD50A54.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5153.image_5F00_thumb_5F00_0AD50A54.png" width="530" height="261" /></a>Il est également possible d’étendre l’« API App » pour ajouter une opération permettant d’obtenir le nom et l’email de la personne connectée. Ainsi, il sera possible depuis l’application universelle de faire un appel à cette méthode et d’afficher l’email de la personne identifiée.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2465.image_3373765B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2465.image_5F00_3373765B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4456.image_thumb_5EBA9E13.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4456.image_5F00_thumb_5F00_5EBA9E13.png" width="530" height="177" /></a>Pour tester l’accès restreint sur l’« API App », on peut utiliser un browser, en déclenchant d’abord une authentification sur l’« API Gateway », via son URL suffixée par « /login/aad » et en utilisant un compte défini sur le « tenant » AAD cible.     <br /><a href="https://nom-de-la-gateway.azurewebsites.net/login/aad">https://nom-de-la-gateway.azurewebsites.net/login/aad</a>     <br />Une fois l’utilisateur identifié sur l’« API Gateway », il devient alors possible de faire usage de l’API.<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2330.image_6E5D39D5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2330.image_5F00_6E5D39D5.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2772.image_thumb_4FA64FEA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2772.image_5F00_thumb_5F00_4FA64FEA.png" width="530" height="66" /></a>Il ne reste plus maintenant qu’à modifier le code de l’application cliente de l’API pour qu’elle puisse passer les paramètres d’authentification requis.</p>  <h1>Evolution du code de l’application Universelle</h1>  <p>L’application mobile peut donner la possibilité de s’authentifier vis-à-vis d’AAD en intégrant la page de Logon comme l’illustre la copie d’écran suivante.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0741.image_0D363E65.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0741.image_5F00_0D363E65.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6038.image_thumb_4413235C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6038.image_5F00_thumb_5F00_4413235C.png" width="530" height="247" /></a>     <br />Le SDK d’« App Service » offre une syntaxe simplifiée pour la gestion du code liée à l’identification de l’utilisateur et au contrôle d’accès qui en découle.     <br />Les URLs de l’« API Gateway » et de l’« API App » doivent être connues de l’application cliente, ainsi que le type de fournisseur d’identité cible, dans notre contexte, la chaîne identifiant l’AAD.</p>  <p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public const string GATEWAY = &quot;<a href="https://nom-de-la-gateway.azurewebsites.net">https://nom-de-la-gateway.azurewebsites.net</a>&quot;;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; public const string API_APP = &quot;<a href="https://nom-de-la-api-app.azurewebsites.net">https://nom-de-la-api-app.azurewebsites.net</a>&quot;;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; public const string AUTH_PROVIDER = &quot;WindowsAzureActiveDirectory&quot;;</p>  <p>C’est dans le constructeur du « MainViewModel » qu’est réalisée l’authentification, par appel au constructeur « AppServiceClient » qui intègre nativement un mécanisme de connexion sur l’« API Gateway ».&#160; </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5417.image_2CE7A8DE.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5417.image_5F00_2CE7A8DE.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7418.image_thumb_2E4BCBB0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7418.image_5F00_thumb_5F00_2E4BCBB0.png" width="530" height="172" /></a>L’authentification est réalisée par l’appel à la méthode asynchrone « LoginAsync(…) », dans laquelle on spécifie le fournisseur d’identité cible.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2161.image1_24F2972D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2161.image1_5F00_24F2972D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4152.image1_thumb_349532EF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4152.image1_5F00_thumb_5F00_349532EF.png" width="530" height="161" /></a>Il est important de bien choisir les conditions permettant de déclencher cette authentification. Il est parfois délicat de se mettre en attente d'un retour d'appel asynchrone à l’intérieur d’un gestionnaire d’évènement associé à la logique de chargement des fenêtres Windows. </p>  <p>J’avais initialement prévu de lancer cet appel sur l’évènement « OnNavigatedTo(…) ». Mais il semble que le modèle de navigation (utilisation d’une page « Shell » permettant d’afficher la page cible en utilisant un contrôle « SplitView ») de l’application rende ce choix inapproprié [la fenêtre de saisie des « credentials » ne s'affichait que si l’appel de la méthode de « LoginAsync(…) » était précédé d’un « Task.Delay » de 50 ms]. Pour contourner cette problématique, l’appel à la méthode est donc déclenché sur la première occurrence de l’évènement « GotFocus(…) » de la page « Shell ».&#160; <br /></p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6232.image_164A7BF9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6232.image_5F00_164A7BF9.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3036.image_thumb_7AA880B3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3036.image_5F00_thumb_5F00_7AA880B3.png" width="530" height="338" /></a>     <br />Une fois authentifié sur l’« API Gateway », on peut alors faire usage de l’« API App » en lui communiquant le jeton délivré par AAD.     <br />La première étape consiste à appeler la méthode « GetUserName() » pour afficher l’email de l’utilisateur dans la page « Welcome ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2313.image_5F06856E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2313.image_5F00_5F06856E.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3835.image_thumb_12CE7BC0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3835.image_5F00_thumb_5F00_12CE7BC0.png" width="530" height="303" /></a>     <br />La méthode « GetUserName() » [de même que la méthode « LaunchSearch() » d’invocation du service de recherche] s’appuie sur un proxy que l’on peut obtenir par la méthode « CreateDocSearchAPI() ». Cette dernière est automatiquement générée par la fonction d’ajout « Azure API App Client » de Visual Studio. Son appel est réalisé depuis l’objet « _appServiceClient() » [lui-même à l’origine de l’appel de la méthode « LoginAsnc() »].     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7585.image_1DFA96BB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7585.image_5F00_1DFA96BB.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8117.image_thumb_02589B76.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8117.image_5F00_thumb_5F00_02589B76.png" width="530" height="325" /></a>Elle offre la possibilité de mettre en cache des jetons d'authentification, afin que l'application n’ait pas besoin d'authentifier l'utilisateur à chaque appel grâce à l’utilisation de la classe « TokenExpiredHandler() ». Le code utilisé pour cette classe est issu de l’exemple <a href="https://github.com/Azure-Samples/API-Apps-DotNet-AzureCards-Sample">Azure Cards</a>. Il s’agit d’une évolution d’une classe fournie initialement pour l’authentification sur « Azure Mobile Services » (nom de code « Zumo », que l’on retrouve d’ailleurs dans l’implémentation…).</p>  <p>Pour gérer le cas où ce jeton expire, la classe « TokenExpireHandler() » dérive de la classe « DelegatingHandler() », une sous-classe de « HttpMessageHandler() ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0525.image_7FB27075.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0525.image_5F00_7FB27075.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1641.image_thumb_4F1EF2BD.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1641.image_5F00_thumb_5F00_4F1EF2BD.png" width="530" height="232" /></a>Lorsque sa méthode « SendAsync » est appelée, elle envoie d'abord le message vers le reste du pipeline HTTP, et si elle obtient une réponse non autorisée (« HttpStatusCode.Unauthorized »), elle tente d'abord de se connecter au service, puis envoie à nouveau la requête avec le jeu d'en-têtes d'authentification approprié.     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3247.image_3A3000FB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3247.image_5F00_3A3000FB.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5353.image_thumb_62623A0D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5353.image_5F00_thumb_5F00_62623A0D.png" width="530" height="468" /></a></p>  <p>Comme la demande est susceptible d’être envoyée plusieurs fois, elle et clonée…    <br />&#160; <br />Nous venons donc d’intégrer un mécanisme d’authentification vis-à-vis d’« Azure Active Directory » sur un service « API App » avec mise en cache des jetons d’authentification depuis une application universelle.     <br />Le prochain et dernier article devrait être plutôt court, je vous indiquerai les éléments permettant de récupérer le code et de déployer rapidement cette application….</p>]]></content:encoded>
    </item>
    <item>
      <title>Implémentation d’une solution fondée sur les Azure App Services : Consommation du service de recherche par une Application Universelle</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/implmentation-dune-solution-fonde-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle</link>
      <pubDate>Sat, 12 Sep 2015 05:11:00 GMT</pubDate>
      <dc:creator><![CDATA[stephgou]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/2015/09/12/implmentation-dune-solution-fonde-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle/</guid>
      <description><![CDATA[Voici donc le cinquième d’une série d’articles consacrée à l'implémentation d’une solution fondée...]]></description>
      <content:encoded><![CDATA[<p>Voici donc le cinquième d’une série d’articles consacrée à l'implémentation d’une solution fondée sur les Azure App Services et qui sera organisée de la façon suivante :    <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/17/implementation-d-une-solution-fondee-sur-les-azure-app-services.aspx"><font color="#0066cc">Solution et choix d’implémentation</font></a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx"><font color="#0066cc">Mise en œuvre d’un service de recherche</font></a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-web.aspx"><font color="#0066cc">Consommation du service de recherche par une application Web</font></a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-exposition-du-service-de-recherche.aspx"><font color="#0066cc">Exposition du service de recherche</font></a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/12/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle.aspx">Consommation du service de recherche par une application Universelle</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-gestion-d-identite.aspx">Gestion d’identité</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-code-et-configuration-de-l-application.aspx">Code et configuration de l’application</a> </p>  <p>L’objectif est de développer une application pouvant s’exécuter sur Windows 10 ou Windows 10 Mobile, en utilisant l’« API App » décrite dans mon précédent article pour accéder aux services Backend.</p>  <h1>Description fonctionnelle de l’application</h1>  <p>Sur le plan fonctionnel, l’application doit permettre de naviguer entre une page de recherche et une page pour le stockage. Nous nous limiterons à l’implémentation de la partie recherche, la page liée au stockage pouvant être développée de façon similaire.    <br />La page « Recherche » offre la possibilité de saisir une chaîne de caractères pour lancer une requête sur l’ensemble des attributs marqués comme étant « searchable » de l’index d’« Azure Search ». Elle doit permettre d’afficher le résultat de cette requête de telle sorte que l’on puisse downloader par un simple clic le document souhaité (avec là encore, incrément du compteur de téléchargement).</p>  <h1>La plateforme UWP    <br />Une plateforme universelle</h1>  <p>Avec Windows 10 et la plateforme UWP (Universal Windows Platform), il est maintenant possible de créer des applications qui s’exécutent, avec le même package, dans un store unique, sur de multiples types de device (Windows 10 mobile, Windows 10, Xbox, Hololens, device IoT,…).</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0602.univapp_5F61F654.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0602.univapp_5F00_5F61F654.png"><img title="univapp" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="univapp" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1185.univapp_thumb_3D319CE1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1185.univapp_5F00_thumb_5F00_3D319CE1.png" width="520" height="226" /></a>     <br />Précédemment, avec les versions Windows 8 et Windows Phone 8, même s’il était possible de partager du code, ce code contenait des directives de compilation permettant de prendre en compte les spécificités de telle ou telle plateforme. En outre, le package était spécifique à chaque plateforme.     <br />L’exécution d’un même code sur PC et sur appareils mobiles est maintenant rendue possible grâce à la convergence des APIs mises à disposition dans une librairie baptisée « Universal Windows ».&#160;&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7510.image201_0125F419.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7510.image201_5F00_0125F419.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5383.image201_thumb_33202554.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5383.image201_5F00_thumb_5F00_33202554.png" width="520" height="529" /></a>     <br />Cette librairie « Universal Windows » apparaît dans la liste des références du projet Visual Studio.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1425.image_5A1C90C0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1425.image_5F00_5A1C90C0.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4643.image_thumb_243CBD08.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4643.image_5F00_thumb_5F00_243CBD08.png" width="520" height="240" /></a></p>  <h2>Les spécificités de l’application universelle « Doc.Search.App »</h2>  <p>L’application « Doc.Search.App » doit fonctionner aussi bien sur un device mobile que sur un PC Windows. Ce choix se traduit par deux pré-requis. </p>  <p>En premier lieu, l’application doit être en mesure de détecter le type de device et de s’adapter en conséquence. Par exemple, l’application offrira la possibilité de faire des retours arrière sur la navigation avec une option de menu « Back » qui n’apparait que sur l’application Windows 10. Cette option offrira le même type de comportement que la touche « Back » sur Windows 10 mobile.</p>  <p>Le second pré-requis est de permettre à l’application d’adapter automatiquement son affichage aux différentes tailles d'écran. La copie d’écran suivante illustre les deux affichages possibles de la même application en fonction du device qui l’exécute.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5430.UIApp_33098391.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5430.UIApp_5F00_33098391.png"><img title="UIApp" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="UIApp" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1007.UIApp_thumb_63580A5A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1007.UIApp_5F00_thumb_5F00_63580A5A.png" width="520" height="228" /></a>     <br />Nous allons maintenant étudier les différentes techniques permettant de satisfaire à ces deux pré-requis.</p>  <h2>Adaptation au device</h2>  <p>L’environnement de développement d’application universelle permet, si on le souhaite, d’écrire du code qui qui ne s’exécute que sous certaines conditions (type de device, version d’API d’extension de la plateforme). Pour ce faire, il suffit d’ouvrir la boîte de dialogue « Gestionnaire de références » et de choisir l'extension du SDK pour la plateforme « Universal Windows » en fonction des familles de devices que le code doit cibler. Bien entendu, l’utilisation de ces extensions de plateforme n'invalide pas les binaires sur les devices qui n’en sont pas la cible.<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7563.image_2E4E0BDB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7563.image_5F00_2E4E0BDB.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2742.image_thumb_654F9C27.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2742.image_5F00_thumb_5F00_654F9C27.png" width="520" height="206" /></a>Ainsi, pour pouvoir utiliser le bouton « Back » sur un device de type Windows 10 Mobile, dans l’application « Doc.Search.App », il faut gérer un évènement de type « BackPressed », ce qui d’après le MSDN, requiert le contrat « Windows.Phone.PhoneContract ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7838.image_64E5F9E3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7838.image_5F00_64E5F9E3.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2570.image_thumb_5AB6F027.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2570.image_5F00_thumb_5F00_5AB6F027.png" width="520" height="217" /></a>     <br />Pour utiliser cette API, le projet « Doc.Search.App » doit donc référencer les extensions Windows Mobile pour la plateforme UWP.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3021.image41_431F42B4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3021.image41_5F00_431F42B4.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6840.image41_thumb_4D095581.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6840.image41_5F00_thumb_5F00_4D095581.png" width="520" height="206" /></a></p>  <h2>Adaptation de l’interface utilisateur</h2>  <p>Comme nous l’avons vu, l’application « Doc.Search.App » doit également proposer une interface adaptée à la taille et à la disposition du device sur lequel elle s’exécute. Pour faciliter ce type d’implémentation, la plateforme UWP offre de multiples évolutions.    <br />De nouveaux contrôles sont venus enrichir la plateforme, comme le « RelativePanel » (un container qui permet de définir une zone dans laquelle on peut positionner et aligner les éléments d'interface les uns par rapport aux autres) ou le « SplitView » un contrôle visuel de navigation. Le « ViewStateManager » a également été amélioré pour permettre de changer facilement la mise en page en fonction des dimensions de l’écran et éviter d’avoir à gérer séparément de multiples projets et définitions d’interface utilisateur.</p>  <h3>« ViewStateManager » - « AdaptiveTrigger »</h3>  <p>En utilisant la classe « AdaptiveTrigger », lorsque la fenêtre est suffisamment réduite, on peut&#160; basculer sur une interface correspondant à celle d’un téléphone mobile et à l’inverse changer d’interface pour s’adapter à un écran plus grand.    <br />Pour ce faire, la classe « AdaptiveTrigger » permet de créer des règles qui déclenchent automatiquement un changement d’état (le « VisualState ») lorsque la hauteur ou la largeur d’une fenêtre dépasse une valeur spécifique.     <br />Lorsque l’on utilise « AdaptiveTrigger » dans le balisage XAML, il n’est plus maintenant plus nécessaire de gérer par code l'évènement « Window.SizeChanged » afin de pouvoir appeler la méthode « VisualStateManager.GoToState ». De plus les effets de ces changements adaptatifs sont directement visibles dans le designer Visual Studio 2015.     <br />Par exemple, pour changer l’affichage d’un mode grille « Paysage » à un mode liste « Portrait », il suffit de déclarer les deux états correspondant à ces « VisualState ».&#160;&#160; <br /></p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0523.image55_7C5D5BBC.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0523.image55_5F00_7C5D5BBC.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5123.image55_thumb_0883F746.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5123.image55_5F00_thumb_5F00_0883F746.png" width="520" height="325" /></a>Ce mécanisme peut être étendu pour permettre d’adapter l’interface de l’application en fonction d’autres critères, comme par exemple la présence d’un bouton « Back » sur le device. C’est ce que propose <a href="https://github.com/dotMorten/WindowsStateTriggers">Morten Nielsen avec ses « WindowsStateTriggers » dont le code source est disponible sur GitHub</a>.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0172.image_4AAF11DC.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0172.image_5F00_4AAF11DC.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4846.image_thumb_4EBBF05F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4846.image_5F00_thumb_5F00_4EBBF05F.png" width="520" height="147" /></a>Pour utiliser cette extension, il suffit alors de la référencer dans l’entête de la page.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3323.image_35567DD6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3323.image_5F00_35567DD6.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7356.image_thumb_0456CD29.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7356.image_5F00_thumb_5F00_0456CD29.png" width="520" height="147" /></a>     <br />Il devient alors possible de l’employer dans la déclaration des éléments participant au déclenchement du changement d’état...</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7128.image_560268DD.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7128.image_5F00_560268DD.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1200.image_thumb_19D92DE6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1200.image_5F00_thumb_5F00_19D92DE6.png" width="520" height="102" /></a></p>  <h3>Le contrôle « SplitView »</h3>  <p>« SplitView » est un contrôle visuel qui permet d’offrir une gestion de la navigation des applications Windows similaire à celle des applications web et autres plates-formes mobiles. Ce contrôle permet d’afficher un menu que l’on peut modifier pour créer un <a href="https://blog.jerrynixon.com/2015/04/implementing-hamburger-button-with.html">menu « hamburger » comme le propose Jerry Nilson dans son blog</a>.     <br />Le contrôle « SplitView » expose deux propriétés. Les boutons de menu et de navigation de l’application sont déclarés dans la propriété « Pane ». La propriété « Content » est, comme son nom l’indique, un « ContentPresenter » pour le « Frame » de l’application. Le contenu de la page est donc directement injecté dans le contrôle lui-même, via la propriété « ContentControl » que l’on définit via un mécanisme de binding.     <br />Ce binding est configuré au niveau de la déclaration XAML du contrôle.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1538.image_64CF2F66.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1538.image_5F00_64CF2F66.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5672.image_thumb_732FC2FA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5672.image_5F00_thumb_5F00_732FC2FA.png" width="520" height="308" /></a>Il est implémenté dans le code la page « Shell.xaml ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2480.image_322664F8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2480.image_5F00_322664F8.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0218.image_thumb_6EDDED88.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0218.image_5F00_thumb_5F00_6EDDED88.png" width="400" height="315" /></a>Il est préférable d’utiliser le contrôle « Splitview » dans une Page dédiée (c’est le rôle de la page « Shell.xaml ») plutôt qu’à la racine (« app.xaml ») : on bénéficie ainsi d’une&#160; meilleure expérience de conception dans Visual Studio. Pour la page « SearchPage.xaml », l’arbre XAML sera donc :     <br /><u>Window.Current.Content &gt; Shell &gt; SplitView &gt; Frame &gt; SearchPage</u></p>  <p>Le contrôle « SplitView » offre plusieurs niveaux d'affichage correspondant à la propriété « DisplayMode ». Dans l’application, en fonction de la taille de l’écran deux modes ont été utilisés : le mode « Compact Overlay « (lorsque le volet de menu est ouvert, il se superpose sur la page contenue dans le contrôle) et le mode « Compact Inline » (lorsque le volet de menu est ouvert, la page contenu se décale et revient à sa position d’origine à la fermeture du volet). Dans les deux cas, le volet reste visible en mode « Compact » lorsqu’il est fermé.    <br />Les changements d’état d’affichage du « SplitView » sont également gérés via le « VisualStateManager ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2781.image_19B8E24C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2781.image_5F00_19B8E24C.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4667.image_thumb_341DF6C5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4667.image_5F00_thumb_5F00_341DF6C5.png" width="520" height="254" /></a></p>  <h2>Déploiement dans le Store</h2>  <p>Avant de distribuer une application depuis le Store, il faut s’assurer de son bon fonctionnement en mode « .NET Native ». « .NET Native » est une technologie de pré-compilation incluse dans Visual Studio 2015 pour les applications universelles. Le principe est de compiler le code « IL » pour obtenir une librairie native avant la mise à disposition de l’application sur le device cible. Cette innovation apporte plusieurs avantages : optimisation des performances, réduction de l’occupation mémoire, indépendance vis-à-vis du runtime .NET… La contrepartie est une durée de compilation qui est plus élevée. Le fonctionnement par défaut (que l’on peut modifier) est donc de réserver l’option de compilation mode « .NET Native » aux configurations de type « Release ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7652.image_4E144798.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7652.image_5F00_4E144798.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6138.image_thumb_06C18257.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6138.image_5F00_thumb_5F00_06C18257.png" width="520" height="221" /></a>     <br />Une fois validé le bon fonctionnement de l’application générée en mode « .NET Native », on peut alors créer un package « .appxupload », en précisant les différentes configurations cibles (x86, x64, ARM).     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4370.image_03AF2462.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4370.image_5F00_03AF2462.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0167.image_thumb_5C0DC999.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0167.image_5F00_thumb_5F00_5C0DC999.png" width="520" height="425" /></a>&#160; <br />Ce package est généré directement depuis Visual Studio, depuis l’option « Store » accessible par un clic droit sur le projet cible « Universal Windows ». Il contient les fichiers binaires MSIL, mais aussi une référence explicite à la version de la configuration « .NET Native » que l’application utilise et qui est déclarée dans le fichier « AppxManifest.xml ».     <br />Il ne reste plus alors qu’à publier le package sur le portail développeur (<a href="https://dev.windows.com">https://dev.windows.com</a>). Il sera alors compilé sur le store avec la même version de l’environnement de compilation « .NET Native » que la version référencée dans Visual Studio.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7701.image_6F5110E9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7701.image_5F00_6F5110E9.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8321.image_thumb_5342E2AF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8321.image_5F00_thumb_5F00_5342E2AF.png" width="520" height="247" /></a></p>  <h1>Implémentation de l’Application Universelle Windows</h1>  <p>Pour implémenter l’application « Doc.Search.App », nous pouvons capitaliser sur les acquis des plateformes précédentes (WPF, Silverlight…). L’application est implémentée selon le respect de modèles éprouvés comme le « pattern M-V-VM » ou le « pattern Command ». Elle est développée en C# et la description de l'interface graphique (fenêtres, bouton, champs de texte, contrôle d’affichage de type liste ou grille) est réalisée en XAML (« Extensible Application Markup Language »), comme nous avons déjà pu voir dans la déclaration du contrôle « SplitView » ou du « VisualStateManager ».</p>  <h2>« Le pattern M-V-VM »</h2>  <p>Le « pattern M-V-VM » (« Model-View-ViewModel ») est dérivé du « pattern MVC ». Dans la logique de décomposition proposée par le « pattern M-V-VM », le « Model » (ou « DataModel ») est responsable des données, la « View » représente la partie visuelle de l'application tandis que le « ViewModel » expose les données et les comportements (notamment les commandes) pour la « View ».    <br />&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5086.image48_1F3F9CCF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5086.image48_5F00_1F3F9CCF.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4278.image48_thumb_5139CE0A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4278.image48_5F00_thumb_5F00_5139CE0A.png" width="340" height="326" /></a></p>  <p>Ainsi la « View » peut utiliser le « ViewModel » dans son « DataContext » pour le binding afin de transformer les données en éléments visuels avec les « DataTemplates ». Le « ViewModel » quant à lui ne fait aucune supposition sur la façon dont va s'opérer le binding. C’est sur ce principe que sont alimentés les contrôles « ListView » et « GridView » que nous verrons par la suite.    <br />Pour afficher la « View » avec les informations souhaitées, il suffit d’alimenter le « ViewModel ». Ce dernier est spécifique pour chaque vue. D’un point de vue technique, il s’agit d’une classe qui implémente le pattern « Observable ».Plus concrètement, il s’agit d’implémenter l’interface « INotifyPropertyChanged » et il est préférable de définir une classe abstraite « BaseViewModel » dont hériteront l’ensemble des « ViewModels » pour y gérer le « PropertyChangedEventHandler ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1462.image_0FB512A7.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1462.image_5F00_0FB512A7.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4628.image_thumb_649526F4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4628.image_5F00_thumb_5F00_649526F4.png" width="520" height="176" /></a>De même, les propriétés de la classe « ViewModel » implémentent également « INotifyPropertyChanged » (ou « DependencyObject ») ou « ObservableCollection » suivant qu’il s’agit d’objets ou de collections, afin de prévenir la vue lors de tout changement des données, comme par exemple la collection de « DocumentProperties » correspondant aux éléments issus de la requête sur le service de recherche.     <br />Lorsque ce « pattern » est mis en œuvre, la « View » ne contient quasiment pas de « code-behind ». Le code réside dans le « ViewModel » et est facile à tester de façon unitaire, ce qui présente un intérêt supplémentaire pour cette approche.</p>  <h2>« Le pattern Command »</h2>  <p>Le « pattern Command » permet de séparer complètement le code initiateur de l'action, du code de l'action elle-même. Très utilisé dans le développement d'interface utilisateur, il permet d'associer plusieurs éléments visuels à la même commande, et de les rendre activables ou non, selon des règles spécifiques (par exemple, le bouton « Rechercher » ne sera actif que si la zone de saisie inclue un critère permettant de filtrer la recherche...).</p>  <p>Plus concrètement, l’implémentation de ce « pattern » se traduit par la déclaration d’une « Command » qui, généralement est réalisée dans le « ViewModel », comme c’est le cas pour la commande « SearchCommand ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5226.image_3BD501F3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5226.image_5F00_3BD501F3.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5001.image_thumb_11885AC9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5001.image_5F00_thumb_5F00_11885AC9.png" width="397" height="515" /></a>     <br />Il ne reste plus alors qu’à faire le binding de cette commande dans la « View » sur le contrôle avec lequel on souhaite l’associer.     <br />&#160; <br />Les commandes sont une implémentation de l'interface « ICommand ». Cette interface expose trois membres :</p>  <ul>   <li>La méthode « Execute(object) » est appelée lorsque la commande est actionnée. Elle comporte un paramètre, qui peut être utilisé pour passer des informations supplémentaires de l'appelant à la commande.. </li>    <li>La méthode « CanExecute(object) » retourne une valeur booléenne. Si la valeur de retour est « true », cela signifie que la commande peut être exécutée. Le paramètre est le même que pour la méthode « Execute ». Le contrôle sera automatiquement désactivé si la méthode « CanExecute » retourne la valeur false. </li>    <li>Le gestionnaire d'événements « CanExecuteChanged » doit être déclenché par l'implémentation de la commande lorsque la méthode « CanExecute » est réévaluée. En XAML, lorsqu'une instance de « ICommand » est liée à une propriété du contrôle commande via une liaison de données, déclencher l'événement « CanExecuteChanged » appelle automatiquement la méthode « CanExecute », et le contrôle va être activé ou désactivé en conséquence. </li> </ul>  <p>L’application « Doc.Search.App » utilise une implémentation « classique » de l’interface « ICommand » avec la classe « RelayCommand ». Cette classe permet d’injecter et de relayer la logique des commandes dans le constructeur de « RelayCommand » via un « delegate », ce qui permet de vérifier si elle peut être exécutée et de la déclencher.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3264.image_092C3785.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3264.image_5F00_092C3785.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7532.image_thumb_178CCB19.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7532.image_5F00_thumb_5F00_178CCB19.png" width="520" height="247" /></a></p>  <p>La déclaration d’une « Command » peut se faire à un autre niveau que celui du « ViewModel ». Par exemple, la commande « UploadCommand », qui permet d’incrémenter le compteur de téléchargement d’un document, doit pouvoir être déclenchée pour chaque « Document » de la collection « DocumentProperties ». Elle doit donc être associée à un contrôle, ici un « HyperlinkButton », lui-même déclaré dans un « DataTemplate ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8468.image_56ED0F5A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8468.image_5F00_56ED0F5A.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2548.image_thumb_1B300758.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2548.image_5F00_thumb_5F00_1B300758.png" width="520" height="52" /></a>     <br />Dans le « ViewModel », lors de l’initialisation de la collection « DocumentProperties », il suffit donc de passer en paramètre une lambda expression permettant de décrire l’action souhaitée lors du déclenchement de la commande.     <br />Dans notre contexte, il s’agit d’appeler l’API de mise à jour du compteur et de relancer la recherche pour que l’affichage soit cohérent avec les nouvelles valeurs (celles du compteur de téléchargement incrémenté mais également les mises à jour pouvant avoir eu lieu en parallèle, l’application n’étant pas mono-utilisateur).</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0743.image_51A0B95A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0743.image_5F00_51A0B95A.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6560.image_thumb_5C60A160.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6560.image_5F00_thumb_5F00_5C60A160.png" width="520" height="147" /></a>La classe « DocumentProperties » étant comme nous allons le voir un peu plus loin, une classe automatiquement générée à partir de la description Swagger de l’« API App », le passage de ce paramètre « Action » sur la classe « DocumentProperties » suppose donc le développement d’une extension sur cette classe incluant la définition de la commande « UploadCommand ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2211.image_399F69A3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2211.image_5F00_399F69A3.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4274.image_thumb_3A3047ED.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4274.image_5F00_thumb_5F00_3A3047ED.png" width="520" height="448" /></a></p>  <h2>L’organisation du code de l’application « Doc.Search.App »</h2>  <p>L’organisation est des plus classiques, un dossier pour les « Views », un pour les « ViewModels », et un pour les extensions.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8546.image_3227D67D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8546.image_5F00_3227D67D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0871.image_thumb_2F157888.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0871.image_5F00_thumb_5F00_2F157888.png" width="250" height="638" /></a></p>  <h2>Les contrôles d’affichage des données</h2>  <p>En fonction des dimensions de l’écran, la page « SearchPage » utilise deux contrôles d’affichage, en mode liste ou en mode grille.</p>  <h3>Le contrôle « ListView »</h3>  <p>Le contrôle « ListView » est nativement conçu pour afficher une énumération d’objets. Par conséquent, il est tout à fait adapté pour restituer la liste des « DocumentProperties ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5123.image_6FB65277.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5123.image_5F00_6FB65277.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0763.image_thumb_4CF51ABA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0763.image_5F00_thumb_5F00_4CF51ABA.png" width="173" height="242" /></a>     <br />Il suffit donc de le déclarer en XAML, d’établir le binding avec la propriété « Documents », et de définir le modèle d’affichage de chacun de ses éléments (l’ « ItemTemplate »).&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3108.image_7CCE85E4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3108.image_5F00_7CCE85E4.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8420.image_thumb_15CCE6DB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8420.image_5F00_thumb_5F00_15CCE6DB.png" width="520" height="78" /></a>L’ « ItemTemplate » est déclaré dans les ressources de la même page.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0654.image_25034FA8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0654.image_5F00_25034FA8.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7506.image_thumb_4F086F32.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7506.image_5F00_thumb_5F00_4F086F32.png" width="520" height="176" /></a>     <br />Il utilise deux objets de type « Converter », l’un pour afficher la tailler du document en unités octales, l’autre pour afficher le nombre de téléchargement (avec ou sans « s », suivant la valeur du compteur…)     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6886.image_72C6B82E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6886.image_5F00_72C6B82E.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6116.image_thumb_38DA05F3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6116.image_5F00_thumb_5F00_38DA05F3.png" width="520" height="353" /></a>&#160; <br />Le contrôle « ListeView » utilise également un « ItemContainerStyleSelector » pour permettre d’alterner le style des lignes affichées dans la liste (pour l’instant sur UWP, il n’y a pas de « Triggers » sur le « DataTemplate » pour alterner les styles comme on pouvait le faire en WPF…).&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4478.image_67C1D939.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4478.image_5F00_67C1D939.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6825.image_thumb_5843E8CC.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6825.image_5F00_thumb_5F00_5843E8CC.png" width="520" height="258" /></a>     <br />Pour utiliser cette classe ou les classes de type « Converter », il suffit de les déclarer dans les ressources de la page.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7848.image_10F1238B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7848.image_5F00_10F1238B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4274.image_thumb_3BCC184E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4274.image_5F00_thumb_5F00_3BCC184E.png" width="520" height="54" /></a></p>  <h3>Le contrôle « GridView »</h3>  <p>Lorsque l’écran le permet, il est plus approprié de pouvoir étendre l’affichage sur une grille, d’où l’intérêt d’utiliser le contrôle « GridView ».&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2465.image_0DE15647.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2465.image_5F00_0DE15647.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2818.image_thumb_43E5D554.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2818.image_5F00_thumb_5F00_43E5D554.png" width="520" height="225" /></a>     <br />De même que pour le contrôle « ListView », il suffit de déclarer une instance de la « GridView » en XAML, d’établir le binding avec la propriété « Documents », de définir le modèle d’affichage de chacun de ses éléments (l’« ItemTemplate ») et définir un « ItemContainerStyleSelector » pour alterner le style des lignes (on utilisera d’ailleurs la même classe que pour la « ListView »).     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2727.image_7^68F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2727.image_5F00_75E0068F.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1460.image_thumb_53437A27.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1460.image_5F00_thumb_5F00_53437A27.png" width="520" height="292" /></a>     <br />L’ « ItemTemplate » est déclaré dans les ressources de la même page. A la différence de l’ « ItemTemplate » défini pour la « ListView », il utilise un « StackPanel » horizontal pour positionner les différents « TextBlock » ainsi que le « HyperlinkButton ».</p>  <h2>Interaction avec l’« API App »</h2>  <p>L’application universelle « Doc.Search.Api » doit pouvoir appeler l’ « API App » exposée sur Azure avec une URL similaire à <a href="https://microsoft-apiapp###############.azurewebsites.net">https://microsoft-apiapp###############.azurewebsites.net</a>. Il s’agit d’une API REST, on pourrait donc naturellement utiliser la classe « HttpClient » incluse dans l’espace de nommage « System.Net.Http ». Mais l’utilisation combinée d’« API App » et Visual Studio propose un modèle de développement nettement plus productif.</p>  <p>En effet, après installation du SDK Azure, Visual Studio offre une fonction d’ajout « Azure API App Client », qui permet de générer un proxy d’invocation de cette API App, avec les modèles associés (dont la classe « DocumentProperties » que nous avons dû étendre pour les raisons précédemment expliquées). Cette fonction est accessible à travers un assistant permettant de choisir l’ « API app » en ligne ou à partir d’un fichier Swagger local.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1205.image_4A7B23EE.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1205.image_5F00_4A7B23EE.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3162.image_thumb_54D169B0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3162.image_5F00_thumb_5F00_54D169B0.png" width="520" height="392" /></a>     <br />Cet assistant télécharge les métadonnées de l’API App et génère une interface typée pour pouvoir gérer les appels à l’API. Une fois la génération de code achevée, un nouveau dossier du nom de l’API App est ajouté à la solution. Il contient le code correspondant à l’implémentation des classes « client » et des modèles de données.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6012.image_4AA25FF4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6012.image_5F00_4AA25FF4.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6862.image_thumb_582D1E4F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6862.image_5F00_thumb_5F00_582D1E4F.png" width="520" height="284" /></a>&#160; <br />L’appel à l’« API App » depuis le code de l’application Universelle est alors grandement simplifié grâce à la mise à disposition de l’objet « DocSearchAPI », qui permet d’accéder à un objet de type « SearchOperations » offrant des méthodes comme « SearchbyNameAsync » permettant de lancer une recherche sur un champ texte ou « Update » assurant la mise à jour du compteur de téléchargement.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6644.image_5F71061C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6644.image_5F00_5F71061C.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7571.image_thumb_35298054.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7571.image_5F00_thumb_5F00_35298054.png" width="520" height="425" /></a>Dans le cas de la méthode « Update », il a été nécessaire de reprendre le code généré dans la classe « SearchOperationsExtensions » pour permettre une invocation asynchrone.     <br />Le code d’origine était le suivant.&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5282.image_67B1FF28.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5282.image_5F00_67B1FF28.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2063.image_thumb_3DD18AF3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2063.image_5F00_thumb_5F00_3DD18AF3.png" width="520" height="80" /></a>Le nouveau code est celui-ci :     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7713.image_294ECC26.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7713.image_5F00_294ECC26.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2084.image_thumb_46577AEE.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2084.image_5F00_thumb_5F00_46577AEE.png" width="520" height="100" /></a></p>  <p>Nous disposons maintenant d’une application universelle capable d’échanger avec une « API App » déployée dans Azure, cette API App permettant d’exposer l’accès au service de recherche « Azure Search ». Dans le prochain article, nous étudierons comment intégrer des mécanismes permettant d’assurer la gestion d’identité de l’ensemble de la solution.</p>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Cloud Data Platform</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/microsoft-cloud-data-platform</link>
      <pubDate>Fri, 28 Aug 2015 14:46:00 GMT</pubDate>
      <dc:creator><![CDATA[stephgou]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/2015/08/28/microsoft-cloud-data-platform/</guid>
      <description><![CDATA[J’ai récemment eu l’occasion de faire une présentation de l’intégralité de la plateforme de données...]]></description>
      <content:encoded><![CDATA[<p>J’ai récemment eu l’occasion de faire <a href="https://stephgou.blob.core.windows.net/share/SG_AZURE_DATA_2015_10_06.pptx">une présentation de l’intégralité de la plateforme de données de Microsoft</a> en me focalisant sur ses composantes Cloud et sur ses solutions de Business Intelligence à destination des utilisateurs. Je souhaitais aborder ce sujet de façon exhaustive tout en offrant une grille de lecture qui illustre la richesse et la cohérence de cette offre. Avec le flux continu de nouveautés sur ce thème, accéléré par les innovations sur Azure, il m’a donc semblé utile de proposer une matrice permettant de structurer cette présentation. C’est le résultat de ces réflexions que je souhaite aujourd’hui partager. L’objectif de ce document n’est donc pas d’étudier dans le détail chacune des composantes de l’offre Microsoft mais d’expliquer à quel type de problématique elles répondent et comment elles peuvent s’imbriquer dans une solution de bout en bout. Evidemment, comme souvent dans ce type d’exercice, certains de mes choix restent sujets à discussion, aussi je vous invite à considérer qu’il s’agit d’une première version et à me faire part de vos commentaires pour l’améliorer…</p>  <h1>Introduction</h1>  <h2>La donnée élément clé du cycle de décision</h2>  <p>Piloter plus efficacement un système d’information grâce à une meilleure utilisation des données suppose :</p>  <ul>   <li>Un changement de culture qui permette à chacun dans l'organisation de prendre des décisions fondées sur ces données </li>    <li>La mise en place de processus et d’outils liés au cycle de vie des données et qui accélèrent leur flux dans toute l'organisation </li> </ul>  <p>L’objectif est donc de pouvoir répondre à différents types de question avec les solutions d’affichage ou de manipulation des données les plus adaptées :</p>  <ul>   <li>Que s’est-il passé ? (Rapports statiques) </li>    <li>Pourquoi est-ce arrivé ? (Rapports interactifs) </li>    <li>Qu’est-il en train de se passer ? (Tableaux de bord temps réel) </li>    <li>Que va-t-il se passer ? (Prédictions) </li> </ul>  <p>&#160;</p>  <h2>Le cycle de vie des données</h2>  <br />  <p>Le cycle de vie des données peut être décomposé selon trois axes :</p>  <ul>   <li>Tout d'abord, comment collecter et ensuite gérer toutes les données entrant dans l'entreprise ? Au volume de données existant dans l’entreprise s’ajoutent aujourd’hui de nouveaux types de données telles que les données non structurées et les flux de données en continu (« streaming »). Il devient donc nécessaire de faciliter la capture et la gestion de ces données pour les préparer aux étapes suivantes de ce cycle. </li>    <li>Une fois ces données recueillies et gérées, il faut être en mesure de les transformer et les analyser et ce, quel que soit leur type, afin de s’assurer qu’elles soient prêtes à être exploitées. </li>    <li>Enfin, il faut pouvoir manipuler ces données en utilisant des outils permettant d’obtenir des réponses visuelles pour chaque question que l’on souhaiterait se poser afin de pouvoir prendre des décisions.      <br /></li> </ul>  <h2><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4276.image_5B5C9E66.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4276.image_5F00_5B5C9E66.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7230.image_thumb_08741BE6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7230.image_5F00_thumb_5F00_08741BE6.png" width="294" height="287" /></a></h2>  <h2></h2>  <h2>&#160;</h2>  <h2>La plateforme de données Microsoft</h2>  <p>Microsoft fournit une plateforme de données cohérente offrant la capacité à gérer le cycle de vie des données de bout à bout afin d’en obtenir la meilleure exploitation. Cette proposition de valeur est rehaussée par la flexibilité du choix des éléments constitutifs de la solution à déployer (Cloud ou OnPremise).    <br />Plus concrètement, si l’on considère l’exemple suivant de flux de donnée au sein d’un système d’information, on se rend rapidement compte de la nécessité de proposer un découpage plus fin que la grille d’analyse sur trois axes précédemment évoquée.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5710.image1_5AAFCB9B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5710.image1_5F00_5AAFCB9B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3666.image1_thumb_5EBCAA1E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3666.image1_5F00_thumb_5F00_5EBCAA1E.png" width="594" height="305" /></a></p>  <p>Sur chacun des axes se distinguent de multiples sous-rubriques en fonction, par exemple, du type de données ou de la nature des transformations opérées. S’y ajoute également la notion de services applicatifs orientés données.    <br />Voici donc la grille d’analyse que je propose afin de passer en revue l’ensemble des composantes de la plateforme de données de Microsoft, en la restreignant volontairement à ses composantes Cloud et aux solutions de Business Intelligence à destination des utilisateurs.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5241.image6_3BFB7261.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5241.image6_5F00_3BFB7261.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0743.image6_thumb_0B67F4A9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0743.image6_5F00_thumb_5F00_0B67F4A9.png" width="605" height="362" /></a></p>  <p>L’objectif de la suite de cet article est donc d’établir une correspondance entre chacun des blocs de ce schéma et une ou plusieurs briques de l’offre Microsoft. </p>  <h1>Collecter et gérer l’information </h1>  <p>Dans un premier temps, il s'agit donc de faire l’inventaire des solutions permettant de capturer et de gérer les données, qu’elles soient structurées ou non, statiques ou propagées en continu, à demeure ou dans le Cloud.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1184.image11_08C1C9A9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1184.image11_5F00_08C1C9A9.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6076.image11_thumb_061B9EA9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6076.image11_5F00_thumb_5F00_061B9EA9.png" width="605" height="364" /></a></p>  <h2>Base de données relationnelles </h2>  <h3>Azure SQL Database </h3>  <p>Azure SQL Database est un service Cloud de mise à disposition de base de données SQL hautement disponible. Toutes les opérations d’administration et d’optimisation correspondantes sont entièrement prises en charge par Microsoft. Le « provisioning » d’un nouveau serveur ou d’une nouvelle base d’une taille maximale de 500 GB est réalisé en l’espace de quelques secondes. Le service est disponible sur l’ensemble des DCs Azure avec un SLA&#160; de 99.99%. Il est facturé en fonction de la consommation (à l’heure) et propose 6 niveaux de performances pour 3 niveaux de service : Basic, Standard, et Premium. Il offre différentes options (« Self-service restore », « Active geo-replication », « Auto failover »). Azure SQL Database est compatible à 100% avec les outils (Management Studio), les frameworks de développement et les bases de données SQL Server 2014.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4784.image16_279ACE38.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4784.image16_5F00_279ACE38.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5241.image16_thumb_0BF8D2F3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5241.image16_5F00_thumb_5F00_0BF8D2F3.png" width="605" height="336" /></a>     <br />&#160; <br />Azure SQL Database offre des fonctions avancées de backup et de réplication inter-datacenters. Les services déjà disponibles comme les Elastic Database Tools, mais surtout les nouveautés comme les Elastic Database Pools, complétés par les Elastic Database Query et les Elastic Database Jobs étendent les possibilités en permettant d’offrir un environnement Database-as-a-Service capable de gérer des charges non prédictibles pour des applications SaaS d’échelle très significative, sans remise en cause des outils de management ou de développement existants.</p>  <h3>SQL Server en IaaS</h3>  <p>Dans l’environnement IaaS (Infrastructure as a Service) proposé avec Azure, il est également possible d’installer et configurer un serveur SQL et ses services associés, de même que l’on peut déployer des bases de données relationnelles propriétaires Oracle, IBM DB2 ou issues du monde de l’Open Source comme MySQL.    <br />En complément des bonnes pratiques habituelles (indexes, plans de maintenances, optimisation du schéma, des requêtes, de tempdb), il faut veiller à prendre en compte certaines spécificités liées au Cloud et notamment optimiser les entrées/sorties sur le stockage, qui, rappelons-le, est un stockage sur un système de fichiers accessible par le réseau. Il est notamment recommandé d’utiliser :</p>  <ul>   <li>un maximum de disques via Disk Stripping (Windows Server Storage Spaces, jusqu’à 64 disques, séparer fichiers de données et fichiers de logs sur des pools distincts) </li>    <li>des achines G ou D (tempdb sur le disque temporaire SSD, Buffer Pool Extension si lecture intensive, sur machines G : In-Memory) </li>    <li>Azure Premium Storage et des machines DS (5000 IOPS par disque, max 50 000 IOPS par machine) </li> </ul>  <p>Des images pré-optimisées sont d’ailleurs fournies nativement dans la Galerie Azure (« SQL Server 2014 Enterprise Optimized for DataWarehousing on Windows Server 2012 R2 » et « SQL Server 2014 Enterprise Optimized for Transactional Workloads on Windows Server 2012 R2 »). </p>  <h2>No SQL </h2>  <h3>Document DB </h3>  <p>Document DB est un service de mise à disposition de base de données NoSQL entièrement géré par Microsoft Azure, à destination des applications Cloud, facilitant le développement itératif et l’évolution du modèle de données. Document DB offre un support natif pour JavaScript, les requêtes SQL, ainsi qu’un traitement transactionnel intégré de JavaScript associé à un modèle fondé sur des documents JSON sans schéma (« Self-describing », « self-contained ») et une indexation automatique.</p>  <p>&#160;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5633.image26_1B9B6EB5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5633.image26_5F00_1B9B6EB5.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0131.image26_thumb_4AECE43F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0131.image26_5F00_thumb_5F00_4AECE43F.png" width="605" height="426" /></a></p>  <h3>Table Storage</h3>  <p>La plateforme Microsoft Azure offre des services de stockage reposant sur un mécanisme scalable et hautement&#160; disponible (avec une géo-réplication en option) permettant de gérer des volumétries de données très conséquentes, de les exposer via API REST pour un usage intra-Datacenter ou extérieur et de les sécuriser. Au sein de ces services, Azure Table Storage offre des fonctions NoSQL de type « clé/valeur » pour les applications devant stocker de grandes quantités de données non structurées. Les tables constituent un service géré certifié ISO 27001, capable d'évoluer automatiquement pour prendre en charge un débit et un volume conséquents jusqu'à 500 téraoctets, accessible de n'importe où, via des API REST ou des API managées.</p>  <h2>Données non structurées </h2>  <h3>Azure Blob Storage </h3>  <p>Autre composante des services de stockage Azure, les Blobs constituent la manière la plus simple de stocker d'importants volumes de données binaires ou de textes non structurés tels que des fichiers vidéo, audio et des images. Il s’agit également d’un service géré certifié ISO 27001, ouvert à des API REST ou managées.</p>  <h3>Azure Premium Storage </h3>  <br />  <p>Avec l'introduction de Premium Storage, Microsoft Azure offre maintenant deux types de stockage durable : Premium et Standard. Le stockage Premium se distingue en permettant de conserver les données sur des disques SSD (« Solid State Drive »). Il est particulièrement adapté aux scénarios OLTP, Big Data et Data Warehousing&#160; (pour des plateformes telles que SQL Server, MongoDB, Cassandra,…) exigeants en termes de performances sur les entrées-sortie et de faible latence (moins de 1ms de latence en lecture). Premium Storage est actuellement disponible pour les Page Blobs et les disques de données utilisés par les Machines virtuelles (Jusqu’à 32 To par machine virtuelle et 50 000 IO/s par machine virtuelle).</p>  <h3>WASB </h3>  <br />  <p>WASB est une implémentation de HDFS (Hadoop Distributed File Store) sur Azure Blob Storage pour le service HDInsight (que nous verrons un peu plus loin dans la suite de cet article). Il constitue une alternative au système de fichiers distribués attachés localement sur les nœuds de calcul d’un cluster Hadoop, avec un accès proposé en utilisant l'URI complet : HDFS: / / &lt;namenodehost&gt;/&lt;path&gt;. Avec WASB, les mêmes données peuvent être stockées dans les Blob Azure. Pour leur usage HDInsight, la syntaxe est : wasb [s]:// &lt;containername&gt;@&lt;accountname&gt;.blob.core.windows.net/ &lt;path&gt;.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6560.image31_6F14CF7F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6560.image31_5F00_6F14CF7F.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0081.image31_thumb_3E8151C7.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0081.image31_5F00_thumb_5F00_3E8151C7.png" width="553" height="456" /></a></p>  <h3>Azure HDInsight HBase </h3>  <br />  <p>Azure HDInsight HBase est le service qui permet de configurer dans Azure des clusters HBase pour stocker de gros volumes de données directement dans le stockage Blob Azure avec une faible latence et les analyser avec des jobs Hadoop. HBase est une base de données Apache Open Source NoSQL « orientée colonne » construite sur Hadoop. Elle fournit un accès aléatoire et une forte cohérence pour grandes quantités de données non structurées et semi-structurées. Les données sont stockées dans les lignes d’une table et regroupées au sein d’une ligne par « famille de colonne ». La scalabilité de HBase (fondé sur le modèle « BigTable » de Google) lui permet de gérer plusieurs pétaoctets de données sur des milliers de nœuds. L’implémentation HDInsight HBase se fonde sur l’architecture de scale-out de HBase pour offrir un sharding automatique des tables, une forte cohérence pour les lectures et écritures et un basculement automatique. La performance est renforcée par la mise en cache en mémoire sur la lecture et un débit élevé en écriture. </p>  <h2>Data Lake </h2>  <br />  <p>Azure Data Lake est un référentiel construit comme un magasin de fichiers distribués à très grande échelle, sans limite de taille, pour le traitement analytique de&#160; données volumineuses. Cela permet de stocker au même endroit de multiples types de données (relationnelles, non relationnelles,…) avant toute définition formelle de schéma ou contrainte de transformation et de les analyser dans leur format natif. Azure Data Lake est compatible avec HDFS (Hadoop Distributed File Store - WebHDFS 2.7), ce qui favorise son intégration avec des solutions Hadoop comme Azure HDInsight, Hortonworks, and Cloudera et le rend accessible par l’ensemble des projets Open Source compatibles HDFS (Spark, Storm, Flume, Sqoop, Kafka, R,...). Il fournit un stockage illimité, la possibilité de stocker des fichiers uniques de taille de l’ordre du pétaoctet avec des accès en lecture/écriture bénéficiant d’une haute fréquence et d’une faible latence, qui sont adaptées aux flux volumineux du Big Data.    <br />&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6840.image36_6DD55802.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6840.image36_5F00_6DD55802.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8831.image36_thumb_52335CBD.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8831.image36_5F00_thumb_5F00_52335CBD.png" width="605" height="288" /></a></p>  <h2>Collecte de données en streaming </h2>  <p>L’ingestion de données peut être gérée de multiples façons dans Azure. </p>  <h3>Azure Service Bus </h3>  <p>Azure Service Bus est un service middleware Azure fondé sur la mise en œuvre d’un pattern de « publication/souscription ». Il permet de séparer le trafic de messagerie entre de multiples rubriques (les Service Bus Topics) afin de maximiser le débit.</p>  <h3>Azure Event Hub</h3>  <p>Event Hub est un service de collecte de données en streaming qui se distingue par sa capacité à proposer un service de réception évolutif, capable de prendre en charge des profils de charge variables, ainsi que des pics causés par une connectivité intermittente. Event Hub offre en effet un mécanisme de type publication-souscription pouvant recevoir des millions d’événements par seconde, tout en conservant l’ordre des évènements. La prise en charge de protocoles standards comme AMQP ou HTTPS permet à de nombreuses solutions de pouvoir interagir avec Event Hub. Des bibliothèques clientes natives existent également pour les plateformes les plus courantes.</p>  <h1>Transformer et analyser les données </h1>  <p>Le monde connecté d’aujourd’hui est défini par le Big Data. En effet, une fois les données collectées, il est nécessaire de les stocker et de les transformer, sachant que les devices et applications connectés peuvent produire des volumes de données très importants.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0207.image41_08A40EC0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0207.image41_5F00_08A40EC0.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5635.image41_thumb_2A233E4F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5635.image41_5F00_thumb_5F00_2A233E4F.png" width="605" height="363" /></a></p>  <h2>Intégration et orchestration </h2>  <h3>Azure Data Factory </h3>  <p>Azure Data Factory est un service Cloud entièrement géré qui le facilite l’intégration des étapes de stockage, de traitement, et de mouvement de données au sein de pipelines de production, évolutifs et fiables. Azure Data Factory facilite la coordination et la gestion des sources de données relationnelles et non-relationnelles à demeure ou dans le Cloud (SQL Server, Azure Blob, Tables, HDInsight, SQL Databases,…). L’accès aux données locales est pris en charge via une passerelle de gestion des données qui permet une configuration et une sécurisation des connexions aux serveurs SQL locaux. Les mécanismes natifs de supervision d’Azure Data Factory permettent de considérablement réduire les coûts opérationnels. </p>  <h3>Power Query</h3>  <p>Power Query est un addin Office qui permet de facilement découvrir des données provenant de sources de données publiques ou d'entreprises. Il inclut des fonctions de recherche, ainsi que des capacités de transformation et fusion de données provenant de plusieurs sources.&#160; <br /></p>  <h2>Data Warehouse et analyse historique des données</h2>  <h3>Azure Data Warehouse (Preview) </h3>  <p>Azure SQL Data Warehouse est une solution Cloud de DataWarehouse-as-a-service bâtie sur l'architecture de traitement massivement parallèle de SQL Server. Ce service peut dynamiquement se redimensionner en fonction des requêtes (avec une facturation à l’usage). Il permet aux utilisateurs d'utiliser leurs compétences T-SQL existantes pour exécuter des requêtes sur un ensemble de données structurées et non structurées, sur des volumes de l’ordre du pétaoctet. Il supporte les requêtes Polybase qui permettent de requêter simultanément des données Hadoop et des données relationnelles et s'intègre avec les outils existants et des solutions comme Azure HDInsight, Azure Machine Learning, Azure Data Factory ou PowerBI.</p>  <h3>Power Pivot    <br /></h3>  <p>Power Pivot est un addin Excel qui permet de modéliser et transformer très rapidement de gros volumes de données (des millions de lignes) grâce au moteur analytique en mémoire x-velocity (algorithmes Vertipaq). Il permet d’importer ces données de multiples sources (bases de données relationnelles, multidimensionnelles, services Cloud, fichiers Excel, fichiers texte et données sur le Web) dans un seul classeur Excel. Il permet de créer des modèles mettant en œuvre des relations entre des données hétérogènes, de créer des colonnes calculées à l'aide de formules et des mesures personnalisées associées à des indicateurs de performance clé, construire des tableaux et des graphiques croisés dynamiques, afin de pouvoir analyser les données.</p>  <h2>Big Data</h2>  <h3>Azure HDInsight&#160; Hadoop </h3>  <p>Azure HDInsight Hadoop est une distribution Apache Hadoop proposée sur le Cloud Microsoft Azure. Hadoop est un framework Open Source Apache qui cible les applications distribuées qui consomment des gros volumes de données. Il est composé de multiples projets. HDFS (Hadoop Distributed File System) est le système de fichiers de Hadoop. Il permet un stockage distribué de fichiers très volumineux (du gigaoctet à plusieurs téraoctets) sur plusieurs ordinateurs. MapReduce est le modèle de programmation qui effectue les opérations de filtrage, de tri et d'autres commandes liées à l'obtention et à la transformation de données en appliquant des algorithmes parallèles et distribués. A ces briques Hadoop s'ajoutent des projets Open Source comme HBase, R, cochon, ruche, Flume, cornac, Avro, Zookeeper, .... Le service HDInsight est architecturé pour gérer et analyser une quantité quelconque de données non structurées ou semi-structurées en proposant des extensions de programmation pour de nombreux langages tels que C#, Java et .NET.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7416.image46_798FC096.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7416.image46_5F00_798FC096.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7875.image46_thumb_70368C13.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7875.image46_5F00_thumb_5F00_70368C13.png" width="643" height="280" /></a></p>  <h3>Azure HDInsight Spark (Preview)</h3>  <p>Azure HDInsight Spark est le service Azure permettant de provisionner, en une dizaine de minutes, un cluster de serveurs pré-configurés avec le framework Open Source Apache Spark afin d’offrir un traitement parallèle de données pour les applications analytiques du Big Data. Les capacités de calcul du moteur Spark sont optimisées pour travailler en mémoire, privilégier la vitesse et la facilité d'utilisation. HDInsight Spark est compatible avec stockage Blob Azure (WASB). Il permet également l’analyse de données en temps réel, grâce aux connecteurs natifs de Spark, qui permettent d'ingérer des données provenant de nombreuses sources (Kafka, Flume, Twitter, ZeroMQ, sockets TCP) et grâce au support d’Azure Event Hub.&#160; <br />&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7384.image59_4FF57240.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7384.image59_5F00_4FF57240.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5432.image59_thumb_4225BCF6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5432.image59_5F00_thumb_5F00_4225BCF6.png" width="520" height="455" /></a></p>  <h2>Analyse temps réel</h2>  <h3>Azure Stream Analytics</h3>  <p>Le service Azure Stream Analytics est issu des travaux de Microsoft Research. Ce moteur de traitement d’événements permet de traiter des informations issues de multiples sources, notamment les données en provenance d’Event Hub, afin d’analyser en « temps réel » des millions d’événements par seconde.&#160; Stream Analytics permet de comparer plusieurs flux en temps réel, et d’intégrer dans l’analyse des valeurs et des modèles historiques en bénéficiant d’un débit élevé, d’une faible latence et d’une résilience garantie. Stream Analytics permet ainsi la détection des anomalies, la transformation des données entrantes, l’alimentation de tableaux de bord en temps réel et offre la possibilité de déclencher une alerte lorsqu’une erreur ou condition spécifique apparaît dans le flux. Stream Analytics propose un langage SQL pour décrire les transformations souhaitées en fournissant une gamme d’opérations sur une fenêtre d’observation temporelle permettant d’appliquer de simples filtres ou des agrégats plus complexes.    <br />&#160; <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6177.image64_66DD5790.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6177.image64_5F00_66DD5790.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8547.image64_thumb_4892A09A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8547.image64_5F00_thumb_5F00_4892A09A.png" width="643" height="331" /></a></p>  <h3>Event Processor Host</h3>  <p>Il est possible de développer un service d’analyse en temps réel des données issues de l’Event Hub en implémentant l’interface IEventProcessor et en enregistrant l’objet correspondant dans un EventProcessorHost, qui est thread safe. Au final, une seule interface à connaître et la beauté de cette interface, c'est qu‘elle masque complètement la complexité de ce type de traitement. En effet, du point de vue du développeur, il suffit de parcourir et filtrer une boucle de messages.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3806.image69_3F396C17.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3806.image69_5F00_3F396C17.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0410.image69_thumb_0EA5EE5F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0410.image69_5F00_thumb_5F00_0EA5EE5F.png" width="605" height="192" /></a></p>  <h3>Azure HDInsight Storm (Preview) </h3>  <p>Azure HDInsight Storm est le service qui permet de configurer dans Azure un cluster Apache Storm managé. Apache Storm est un système de calcul Open Source distribué et résilient, pour le traitement de données, en temps réel, avec Hadoop. Ces données peuvent être issues de files d’attente Service Bus ou Hub Event. Les clusters HDInsight Storm peuvent également être configurés sur un réseau virtuel Azure (VNET) extensible avec un VPN, afin d’offrir une communication sécurisée vis-à-vis de ressources localisées au sein d’un DataCenter privé.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1106.image74_20F145D2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1106.image74_5F00_20F145D2.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3542.image74_thumb_0959985F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3542.image74_5F00_thumb_5F00_0959985F.png" width="604" height="337" /></a></p>  <h2>Analyse prédictive</h2>  <p>Azure Machine Learning est un service intégralement géré par Microsoft Azure. Il permet de prévoir les futurs comportements ou tendances, en fondant ces analyses prédictives sur des données précédemment collectées. Azure Machine Learning offre une expérimentation rapide pour créer un meilleur modèle avec l’aide de bibliothèques de modèles réutilisables. Il permet une composition visuelle de bout en bout sur le flux de données et supporte l’environnement de programmation Open Source « R » pour les statistiques et l’exploration de données. Enfin, il est possible de déployer le modèle comme un Service Web hébergé dans Azure.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2450.image82_6DB79D19.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2450.image82_5F00_6DB79D19.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4885.image82_thumb_0F36CCA9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4885.image82_5F00_thumb_5F00_0F36CCA9.png" width="605" height="257" /></a></p>  <h1>Manipuler l’information et prendre des décisions</h1>  <p>Les données ne commencent à avoir de la valeur qu’à partir du moment où l’on est en mesure de les manipuler pour en retirer de nouvelles connaissances. Les solutions à mettre en place doivent proposer une vue de bout à bout de tous les types de données (structurées, non structurées, précédemment archivées ou mis au rebut).    <br />Même si ces données ont été intégrées ou transformées, elles ne sont pas nécessairement localisées au même endroit : il faut donc pouvoir offrir un accès sécurisé à de multiples sources (données internes ou résidant dans les solutions SaaS et autres emplacements externes). Chacun doit avoir la possibilité de pouvoir prendre des décisions sur la base de ces données. Il faut donc rendre ces données disponibles en fonction de l’utilisateur, de ses niveaux d’accès, de ses besoins, et du moment. Enfin, ces données doivent être accessibles en situation de mobilité quel que soit le lieu, l’instant, ou le device utilisé…</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8787.image87_45A77EAB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8787.image87_5F00_45A77EAB.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7573.image87_thumb_57F2D61E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7573.image87_5F00_thumb_5F00_57F2D61E.png" width="605" height="361" /></a></p>  <h2>Power View </h2>  <p>Power View est un addin Excel qui permet de facilement créer des rapports visuels interactifs et qui offre une vue analytique par le biais de cartes interactives ou graphiques pour faciliter l’exploration des données et la collaboration autour de la visualisation de ces données.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/3515.image92_275F5866.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/3515.image92_5F00_275F5866.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0878.image92_thumb_5CFA352F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0878.image92_5F00_thumb_5F00_5CFA352F.png" width="602" height="451" /></a></p>  <h2>Power Map</h2>  <p>Power Map est un addin Excel qui s’inspire des travaux de Microsoft Research sur le projet « WorldWide Telescope ». Il ajoute une nouvelle dimension en offrant la possibilité de tracer visuellement des données géographiques et temporelles sur Bing Maps, d'analyser ces données en 3D et de créer des visites interactives que l’on peut partager.</p>  <h2>Power BI</h2>  <p>Power BI est service Cloud d'exploration de données visuelles qui permet d'analyser les données et créer des rapports. Il intègre des fonctions d’acquisition et de préparation des données similaires à celles de Power Query. Il permet la consolidation de plusieurs sources de données, à demeure ou dans le Cloud, pour définir leur structure, leur transformation, leur analyse, leur exploration, puis leur partage sécurisé au sein de l’organisation. Les rapports interactifs sont construits en ligne ou avec Power BI Desktop par simple drag-and-drop d’une large gamme de contrôles de visualisation de données. Ils sont accessibles en ligne (HTML5), via Power BI Desktop ou depuis une application mobile.</p>  <p>&#160;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5468.image97_0B75D581.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5468.image97_5F00_0B75D581.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5468.image97_thumb_56D809F6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5468.image97_5F00_thumb_5F00_56D809F6.png" width="454" height="229" /></a></p>  <h2>Datazen</h2>  <p>Datazen est une solution récemment acquise par Microsoft, qui offre des fonctions très avancées de visualisation interactive de données et d’indicateurs de performance sur des scénarios connectés ou déconnectés pour tout type de device.    <br />    <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4341.image105_0D48BBF9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4341.image105_5F00_0D48BBF9.png"><img title="image[105]" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image[105]" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8053.image105_thumb_4A6C777E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8053.image105_5F00_thumb_5F00_4A6C777E.png" width="504" height="284" /></a></p>  <h1>Services de données d’application</h1>  <p>Lorsque l’on souhaite bâtir une application dans le Cloud, il y a de multiples points à considérer pour implémenter certaines fonctions comme la recherche, ou pour améliorer la réactivité et les performances par des mécanismes de cache. Il devient alors pertinent de s’intéresser à la mise en œuvre de services applicatifs orientés données (cache, service de recherche, …).</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6305.image107_2ECA7C39.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6305.image107_5F00_2ECA7C39.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1030.image107_thumb_5049ABC8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1030.image107_5F00_thumb_5F00_5049ABC8.png" width="605" height="359" /></a></p>  <h2>Cache Applicatif</h2> En fournissant un accès optimisé sur les données via un stockage en mémoire (localement ou sur un service dédié), les différents services Microsoft Azure Cache offrent un premier niveau de réponse à ce type d’exigence. Les services de Cache ajoutent des options de contrôle et de résilience supplémentaires telles que la haute disponibilité pour garantir que les données mises en cache soient conservées.   <br />  <h3>In-Role Cache</h3> Il s'agit d'un cache auto-hébergé, accessible via le SDK Azure permettant d'effectuer la mise en cache à l'aide d'une instance dédiée Web Role ou de Worker Role pour une application déployée dans un Cloud Service PaaS. Cette solution offre une certaine flexibilité en termes d'options de déploiement et de taille, mais se limite à ce contexte d’utilisation. La facturation correspond à celle du Cloud Service hébergeant le Cache.   <br />  <h3>Azure Redis Cache</h3>  <p><a href="https://blogs.msdn.com/b/stephgou/archive/2014/09/11/azure-redis-cache.aspx">Azure Redis Cache</a> est un service construit sur la solution Open Source Redis Cache. Ce service donne accès à un cache Redis sécurisé, dédié et géré par Microsoft. A la différence du In-Role Cache, un cache créé à l'aide d'Azure Redis Cache est accessible depuis n'importe quel type d’application Azure.</p>  <h2>Service de recherche </h2>  <br /><a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx">Azure Search</a> est un service Cloud qui facilite l'ajout de fonctions de recherche à un site web ou une application. Ce service est fondé sur le moteur de recherche <a href="https://www.elastic.co/products/elasticsearch">Elasticsearch</a>. Il offre donc la recherche « full-text », ainsi que des fonctions plus avancées telles que les suggestions de requête basées sur une saisie partielle, le surlignage et la navigation à facettes. Il intègre le support du langage naturel, en appliquant des règles linguistiques en fonction de la langue spécifiée. Enfin, et surtout, il est entièrement géré. Cette approche « Search as a Service » élimine la complexité liée à la configuration ainsi qu’à la gestion d’un index de recherche et permet de bénéficier d’une haute disponibilité sur le service, de son adaptation à la charge (capacité de stockage et de recherche) et des mises à jour du moteur.   <br />  <h1>Conclusion</h1> Microsoft propose une plate-forme de données complète et cohérente pour gérer le cycle de vie des données en offrant une totale flexibilité sur le choix de solution à demeure ou dans le Cloud. Le périmètre traité dans cet article était volontairement limité aux services orientés données de la plateforme Cloud Microsoft Azure et leur utilisation conjointe avec la gamme d’outils Microsoft de Business Intelligence accessibles en mode Self-Service.   <br />La matrice suivante établit une correspondance entre chacune des dimensions liées au cycle de vie des différents types de données et la ou les briques proposées par Microsoft pour répondre aux problématiques sous-jacentes.   <br />  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0160.image119_2AE2490B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0160.image119_5F00_2AE2490B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2061.image119_thumb_07B76F0A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2061.image119_5F00_thumb_5F00_07B76F0A.png" width="605" height="363" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Implémentation d’une solution fondée sur les Azure App Services : Exposition du service de recherche</title>
      <link>https://docs.microsoft.com/archive/blogs/stephgou/implmentation-dune-solution-fonde-sur-les-azure-app-services-exposition-du-service-de-recherche</link>
      <pubDate>Fri, 28 Aug 2015 05:43:00 GMT</pubDate>
      <dc:creator><![CDATA[stephgou]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/stephgou/2015/08/28/implmentation-dune-solution-fonde-sur-les-azure-app-services-exposition-du-service-de-recherche/</guid>
      <description><![CDATA[Voici donc le quatrième d’une série d’articles consacrée à l'implémentation d’une solution fondée...]]></description>
      <content:encoded><![CDATA[<p>Voici donc le quatrième d’une série d’articles consacrée à l'implémentation d’une solution fondée sur les Azure App Services et qui sera organisée de la façon suivante :    <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/17/implementation-d-une-solution-fondee-sur-les-azure-app-services.aspx">Solution et choix d’implémentation</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/19/implementation-d-une-solution-fondee-sur-les-azure-app-services-mise-en-uvre-du-service-de-recherche.aspx">Mise en œuvre d’un service de recherche</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-web.aspx">Consommation du service de recherche par une application Web</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/08/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-exposition-du-service-de-recherche.aspx">Exposition du service de recherche</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/12/implementation-d-une-solution-fondee-sur-les-azure-app-services-consommation-du-service-de-recherche-par-une-application-universelle.aspx">Consommation du service de recherche par une application Universelle</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/21/implementation-d-une-solution-fondee-sur-les-azure-app-services-gestion-d-identite.aspx">Gestion d’identité</a>     <br />•<a href="https://blogs.msdn.com/b/stephgou/archive/2015/09/28/implementation-d-une-solution-fondee-sur-les-azure-app-services-code-et-configuration-de-l-application.aspx">Code et configuration de l’application</a> </p>  <p>Le service « Azure Search » offrant nativement une interface REST, on pourrait s’interroger sur l’intérêt de développer et exposer une API supplémentaire pour accéder à ce service. De multiples raisons peuvent justifier ce choix, comme par exemple la possibilité d’appliquer des transformations de format ou de sécuriser l’accès à cette API en se fondant sur un mécanisme d’authentification spécifique. Se pose alors la question de savoir comment nous allons développer et héberger cette API.</p>  <h1>API Apps </h1>  <p>Avec la prolifération des périphériques mobiles, les entreprises doivent proposer des API permettant aux développeurs d'applications internes ou non, d’offrir de nouvelles expériences spécifiquement conçues pour ce type de device. Cette évolution est facilitée par la généralisation de l’adoption du style d’architecture REST, des standards liés (HTML, JSON, ….) et des composantes technologiques associées en termes d’implémentation, de sécurité et d’infrastructure.    <br />Pour accélérer cette transformation digitale, les « Azure API Apps » facilitent la création et la publication d’API. Elles permettent de les créer dans différents langages (ASP.NET, Java, PHP, Node.js, Python,…), de les déployer et de les héberger sur une infrastructure Microsoft Azure.     <br />Elles offrent une gestion d’accès simplifiée vis-à-vis du service d’authentification intégré Azure Active Directory ou d’autres services tiers (comme les fournisseurs d’identité associés à Facebook ou Twitter) sans nécessité d’apporter de modification au code de l’API (via API Gateway : nous reviendrons sur ce sujet ultérieurement).     <br />Elles permettent une connectivité hybride étendue grâce à plusieurs dizaines de connecteurs (SaaS &amp; BizTalk) afin de simplifier le code à écrire pour assurer l’interaction avec SalesForce, Office 365, Twitter, Facebook, Dropbox… Dans le cadre de notre solution, nous n’aurons pas à utiliser cette capacité.     <br />Enfin, elles augmentent la productivité en proposant un support natif des métadonnées Swagger&#160; (documentation interactive et de détection d’API REST) pour la définition de l’API, ce qui permet d’offrir, grâce au SDK d’ « API Apps », des mécanismes de génération automatique de code client dans divers langages, par exemple C#, Java et JavaScript. Comme nous allons le voir, ce service est totalement intégré à Visual Studio, ce qui facilite grandement la publication et le debugging d'une API.     <br /></p>  <h1>Développement d’une API App</h1>  <p>Après installation du SDK Azure, Visual Studio (2013 ou 2015) propose un modèle pour le développement d’ « API App ». L’utilisation de ce modèle permet de créer un projet pré-configuré avec le code et les librairies requises.&#160; <br /></p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8625.image_4551751E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8625.image_5F00_4551751E.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1464.image_thumb_1451C471.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1464.image_5F00_thumb_5F00_1451C471.png" width="602" height="449" /></a>     <br />L’implémentation du service proprement dit est similaire à celle d’une Web API. Il s’agit dans un premier temps de décider des fonctions que l’on souhaite exposer. Dans notre contexte, nous nous limiterons dans un premier temps à des fonctions liées au contexte du service de recherche en les limitant à un usage de consultation. Il nous faut donc pouvoir remonter la liste des documents correspondant aux critères de recherche mais aussi pouvoir incrémenter le compteur correspondant aux nombres de demandes de téléchargement.     <br />La méthode de recherche sur le nom du document (voire sur le content-type si l’on passe l’extension) renvoie une collection de DocumentProperties. De même que pour une Web API, il faut choisir le protocole d’exposition du service REST. Cette méthode sera exposée sur la base de l’utilisation d’un verbe http GET. On peut utiliser la déclaration d’attribut dans le Controller comme [HttpGet] ou respecter la convention qui consiste à préfixer la méthode par Get.<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7827.image_73EB5E87.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7827.image_5F00_73EB5E87.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5633.image_thumb_310F1A0D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5633.image_5F00_thumb_5F00_310F1A0D.png" width="520" height="56" /></a>Le paramètre « name » de la méthode « SearchByName » correspond au nom du paramètre « {name} » renseigné dans le fichier « WebApiConfig.cs ».</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2134.image_1C20284B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2134.image_5F00_1C20284B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0216.image_thumb_38BCA41E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0216.image_5F00_thumb_5F00_38BCA41E.png" width="520" height="344" /></a></p>  <p>Par défaut le service API App refuse l’utilisation de du caractère « * »&#160; et renvoie le message « A potentially dangerous Request.Path value was detected from the client (*) ». L’implémentation de la méthode « SearchByName » est donc aménagée pour permettre l’envoi de requête sur l’ensemble des fichiers, le « * » étant alors remplacé par un « #@! »… </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7851.image_500F5AA2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7851.image_5F00_500F5AA2.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1805.image_thumb_2CE480A1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1805.image_5F00_thumb_5F00_2CE480A1.png" width="520" height="349" /></a>     <br />On peut alors très simplement tester en local l’utilisation de cette méthode. Par exemple, l’appel REST correspondant à la recherche de l’ensemble des documents commençant par la lettre A sera : <a href="https://localhost:15244/api/Search/A">https://localhost:15244/api/Search/A</a>     <br />La méthode d’incrémentation du compteur, baptisée « Update » est exposée via le verbe http PUT puisqu’il s’agit de la mise à jour d’une ressource existante.<img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1832.image_thumb_301B89EB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1832.image_5F00_thumb_5F00_301B89EB.png" width="520" height="48" />L’implémentation de cette méthode est fondée sur le même principe que son équivalent dans l’application Web (Utilisation de l’objet « SearchIndexClient »,&#160; et indexation par lot sur un batch composé d’une seule action de création).</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6082.image_2D755EEB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6082.image_5F00_2D755EEB.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/4810.image_thumb_6A2CE77B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/4810.image_5F00_thumb_5F00_6A2CE77B.png" width="520" height="188" /></a></p>  <h1>Déploiement d’une API App</h1>  <p>Le déploiement est réalisé directement depuis Visual Studio. L’ « API App » ainsi déployée s’inscrit dans un groupe de ressources. Un groupe de ressources est un conteneur logique destiné à faciliter la gestion du cycle de vie d’un regroupement de multiples ressources Azure : dans notre cas, une instance de service « Azure Search », une « Web App », et une « API App » ont été ajoutées au même groupe de ressources. Une fois l’ « API App » publiée, elle peut être configurée dans le nouveau portail. Il est également possible de downloader le fichier Swagger.    <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5751.image_1507DC3F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5751.image_5F00_1507DC3F.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/1184.image_thumb_74832D3D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/1184.image_5F00_thumb_5F00_74832D3D.png" width="520" height="202" /></a></p>  <h1>Swagger</h1>  <p>L’« API App » peut être debuggée directement depuis Visual Studio (qu’elle s’exécute en local ou dans le Cloud). Il est également possible de la tester depuis l’interface Swager. Swagger est un framework offrant des mécanismes de génération de documentation et de code à partir d’une API REST. Il est possible d’étendre une « API App » avec Swagger. Le projet créé à partir du template « API App » dans Visual Studio intègre une référence à l’assembly&#160; « Swashbuckle » (un « Swasjbuckler » est un héros de film de cape et d’épée…).&#160; <br /></p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2376.image21_61A8FA02.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2376.image21_5F00_61A8FA02.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/0317.image21_thumb_4A7D7F84.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/0317.image21_5F00_thumb_5F00_4A7D7F84.png" width="400" height="453" /></a>     <br />Par défaut, le site Swagger n’est pas exposé.&#160; Pour l’activer, il suffit de dé-commenter le code suivant dans le fichier « SwaggerConfig.cs ».     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2425.image14_281F9117.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2425.image14_5F00_281F9117.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8787.image14_thumb_5366B8CF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8787.image14_5F00_thumb_5F00_5366B8CF.png" width="807" height="153" /></a> L’URL du site Swagger associée à l’API App est obtenue en suffixant l’URL du site correspondant par « /swagger ». On dispose alors de l’interface suivante :&#160;&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7416.image_664744FE.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7416.image_5F00_664744FE.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5611.image_thumb_4319DA4C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5611.image_5F00_thumb_5F00_4319DA4C.png" width="520" height="236" /></a>&#160; <br />Pour tester une API, il suffit alors d’étendre l’opération que l’on souhaite cibler.&#160;&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8712.image_62CB44C5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8712.image_5F00_62CB44C5.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/5543.image_thumb_092FB8C2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/5543.image_5F00_thumb_5F00_092FB8C2.png" width="520" height="379" /></a>     <br />On dispose ainsi d’une console permettant de renseigner les paramètres de la méthode et de l’exécuter, comme l’illustre la copie d’écran suivante pour le GET :&#160;&#160; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/6837.image_25CC3495.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/6837.image_5F00_25CC3495.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/2376.image_thumb_491E4A9C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/2376.image_5F00_thumb_5F00_491E4A9C.png" width="524" height="455" /></a>Et cela offre naturellement la possibilité de tester très simplement le PUT.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/8535.image_1B5833EA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/8535.image_5F00_1B5833EA.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/75/63/metablogapi/7206.image_thumb_0356E433.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-75-63-metablogapi/7206.image_5F00_thumb_5F00_0356E433.png" width="520" height="433" /></a></p>  <p>Maintenant que le service de recherche est exposé via une API App déployée dans Azure, dans le prochain article, nous nous intéresserons à la façon dont nous allons pouvoir le consommer depuis une application Windows Universelle, une bonne occasion d’explorer ensemble les nouveautés de la plateforme UWP….</p>]]></content:encoded>
    </item>
  </channel>
</rss>