<?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>Wu Shuai's Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/wushuai/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/wushuai/feed.xml</link>
    <description>Modern Development</description>
    <lastBuildDate>Mon, 08 Apr 2019 13:54:09 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Get Started with AKS CI / CD in Azure DevOps</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/get-started-with-aks-ci-cd-in-azure-devops</link>
      <pubDate>Sun, 02 Dec 2018 23:15:29 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1885</guid>
      <description><![CDATA[The easiest way to build Azure kubernetes service CI / CD pipeline is to use Azure DevOps project...]]></description>
      <content:encoded><![CDATA[The easiest way to build Azure kubernetes service CI / CD pipeline is to use Azure DevOps project which has provided rich templates for various of programming language. On the other hand, I also got some questions regarding to how to setup the pipelines from empty template. In this article, I am going to use a very simple nodejs web application as example to demonstrate how to perform AKS CI / CD in Azure DevOps. 
<h3>
Prerequisite
</h3>
An Azure Kubernetes Service cluster
An Azure Container Registry account
An Azure DevOps account

<h3>
Sample Project
</h3>
The sample project is a simple nodejs application based on express, it will return pod name per request. Below is the code structure.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/vscode-1024x385.png" alt="" width="1024" height="385" class="alignnone size-large wp-image-1875" />
Below is the code for index.js
<pre>
'use strict';

const express = require('express');

// Constants
const PORT = 80;
const HOST = '0.0.0.0';

// App
const app = express();
app.get('/', (req, res) =&gt; {
  res.send('Hello from ' + process.env.PODNAME);
});

app.listen(PORT, HOST);
console.log(`Running on https://${HOST}:${PORT}`);
</pre>
Below is the code for Dockfile
<pre>
FROM node:carbon
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY src /app
EXPOSE 80
CMD [ "node", "index.js" ]
</pre>
Below is the code for nodejsapp.yaml. It will deploy nodejs-front as pod and expose it by an external load balancer. Notice that the predefined variable Build.BuildId is used to define the image tag and we will update it in build pipeline.
<pre>
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-front
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodejs-front
  template:
    metadata:
      labels:
        app: nodejs-front
    spec:
      containers:
      - name: nodejs-front
        image: jacregistry1.azurecr.io/aksdevops-nodejs:#{<strong>Build.BuildId</strong>}#
        ports:
        - containerPort: 80
        env:
          - name: PODNAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
      imagePullSecrets:
      - name: <strong>registrysecret</strong>
---
apiVersion: v1
kind: Service
metadata:
  name: nodejs-front
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nodejs-front
</pre>

<h3>
Build Pipeline
</h3>
In the build pipeline, we will build and publish the image to an existing azure container registry, then publish nodejsapp.yaml into artifact which will be used in release pipeline later. Before moving forward, please make sure "<strong>Hosted Ubuntu 1604</strong>" is selected as the build agent. 

<h4><strong>npm install</strong></h4>
I have added "<strong>npm install</strong>" as the first task in the pipeline, though it is not really needed in this demo project, it can be followed by other npm tasks such as unit test in production project.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/npm-1024x524.png" alt="" width="1024" height="524" class="alignnone size-large wp-image-1815" />

<h4><strong>Replace tokens in Yaml</strong></h4>
As mentioned previously, the predefined variable Build.BuildId is used as placeholder in nodejsapp.yaml, we will update it by current build id in the below "<strong>Replace Token</strong>" task.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/replace-1024x484.png" alt="" width="1024" height="484" class="alignnone size-large wp-image-1865" />

<h4><strong>Build an image</strong></h4>
In order to build an image, we will use "<strong>Docker</strong>" task here, alternatively, Docker Compose task is available as well. Make sure the azure subscription can be selected successfully, if the expected subscription doesn't appear, please follow the "Manage" link to create the service principle manually. Make sure build is selected as command and also fill in the Dockfile path shown below.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/build-1024x472.png" alt="" width="1024" height="472" class="alignnone size-large wp-image-1795" />

<h4><strong>Push an image</strong></h4>
Similar to the build an image task, just make sure correct azure container registry is filled in, and push is selected as the command.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/push1-1024x478.png" alt="" width="1024" height="478" class="alignnone size-large wp-image-1835" />

<h4><strong>Copy Yaml</strong></h4>
Create a "<strong>Copy Files</strong>" task to copy the nodejsapp.yaml to staging directory shown below.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/copy-1024x505.png" alt="" width="1024" height="505" class="alignnone size-large wp-image-1805" />

<h4><strong>Publish Artifact</strong></h4>
Lastly, a "<strong>Publish Artifact</strong>" task is created to publish the staging directory, the yaml file is packaged into artifact and will be used in release pipeline to perform the deployment.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/publish-1024x473.png" alt="" width="1024" height="473" class="alignnone size-large wp-image-1825" />

<h3>
Release Pipeline
</h3>
Make sure the above build pipeline is selected as the source of this release pipeline and then create an empty stage environment, <strong>Hosted Ubuntu 1604</strong> should be used as agent as well. Create a "<strong>Deploy to Kubernetes</strong>" task and select apply as the command, that means the task will perform kubectl apply command in the background. In this task, we also need fill in the secret section, it will execute kubectl create docker-registry secret in the background for the kubernetes cluster to fetch the image from container registry via the secret. 
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/release1-1024x771.png" alt="" width="1024" height="771" class="alignnone size-large wp-image-1895" />




]]></content:encoded>
    </item>
    <item>
      <title>Get Started with Cognitive Service Container in IoT Edge</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/get-started-with-cognitive-service-container-in-iot-edge</link>
      <pubDate>Sat, 17 Nov 2018 02:52:09 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1765</guid>
      <description><![CDATA[In order to enable the developers to build AI system based on Azure cognitive service with low...]]></description>
      <content:encoded><![CDATA[In order to enable the developers to build AI system based on Azure cognitive service with low latency and better data governance, the cognitive services just launched Face, Recognize Text, and Text Analytics containers in public preview. That means the developers can use exactly the same intelligent APIs that are available in Azure, but with the flexibility that comes with Docker containers which can be deployed on premise, on the edge, or in the cloud. For more related information, please refer to <a href="https://azure.microsoft.com/en-us/blog/getting-started-with-azure-cognitive-services-in-containers/">Getting started with Azure Cognitive Services in containers</a> and <a href="https://azure.microsoft.com/en-us/blog/bringing-ai-to-the-edge/">Bringing AI to the edge</a>.

This article is about how to bring cognitive service container into iot edge, and I will use text analytics for example. Before moving forward, please refer to https://docs.microsoft.com/en-us/azure/cognitive-services/text-analytics/how-tos/text-analytics-how-to-install-containers firstly. As you can see, the usage of cognitive service container is very straightforward to create cognitive service in azure, retrieve the key and endpoint, then just run it as a normal container with three command parameters:

<strong>ApiKey</strong>
The API key of the Text Analytics resource used to track billing information. The value of this option must be set to an API key for the provisioned Text Analytics Azure resource specified in Billing.

<strong>Billing</strong>
The endpoint of the Text Analytics resource used to track billing information. The value of this option must be set to the endpoint URI of a provisioned Text Analytics Azure resource.

<strong>Eula</strong>
Indicates that you've accepted the license for the container. The value of this option must be set to accept.

In order to bring the cognitive service container into iot edge, we just need create an iot edge custom module and specify Eula, Billing and ApiKey in container create options shown below.
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/Capture1.png" alt="" width="379" height="454" class="alignnone size-full wp-image-1775" />
In order to see the effect easily, we can map the container's port to host machine shown below:
<pre />
{
  "HostConfig": {
    "PortBindings": {
      "5000/tcp": [
        {
          "HostPort": "8085"
        }
      ]
    }
  },
  "Cmd": [
      "Eula=accept",
      "Billing=https://westus.api.cognitive.microsoft.com/text/analytics/v2.0",
      "ApiKey=2bae6d626a3c44cf9e86dec697498611"
  ]
}
</pre>
Then, I will be able to access the text analytics swagger API via host port directly which means the cognitive service container is running well now. And, we can either call the REST API operations available, or use the Azure Cognitive Services Text Analytics SDK client library for further text analytics operations. 
<img src="https://msdnshared.blob.core.windows.net/media/2018/11/tempsnip-1024x506.png" alt="" width="1024" height="506" class="alignnone size-large wp-image-1785" />]]></content:encoded>
    </item>
    <item>
      <title>How to Visualize Azure Resource Metrics in Power BI</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/how-to-visualize-azure-resource-metrics-in-power-bi</link>
      <pubDate>Sun, 28 Oct 2018 09:47:13 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1715</guid>
      <description><![CDATA[The ability to monitor all the azure resources' status is very important that makes the operator...]]></description>
      <content:encoded><![CDATA[The ability to monitor all the azure resources' status is very important that makes the operator understand how all the services are going on. Azure monitor has provided rich visualization capability that we can monitor these services' metrics inside Azure Monitor blade or pin to dashboard.

<img width="1024" height="623" class="alignnone size-large wp-image-1725" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/48-1024x623.png" />

&nbsp;

On the other hand, as a widely used analysis service that provides rich visualization and report functionalities, there is also requirement to visualize these metrics in Power BI, then the operator can see the metrics in existing report as well. In order to make it, all we need is to create Log Analytics.

<img width="440" height="500" class="alignnone size-full wp-image-1745" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/64.png" />

Basically, log data collected by Azure Monitor is stored in Log Analytics which collects telemetry and other data from a variety of sources and provides a query language for advanced analytics. Notice that Log Analytics was previously treated as its own service in Azure. It is now considered a part of Azure Monitor and focuses on storage and analysis of log data using its query language.

Next, we need connect the target azure resource with the log analytics. I have used a web app in the below example. Actually, almost all the azure services have been supported in log analytics and the supported metrics are documented in <a href="https://docs.microsoft.com/en-us/azure/monitoring-and-diagnostics/monitoring-supported-metrics?toc=/azure/azure-monitor/toc.json">Supported metrics with Azure Monitor</a>. 

<img width="1024" height="277" class="alignnone size-large wp-image-1685" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/116-1024x277.png" />

&nbsp;

Navigate to the Log blade, we can query the metrics as we want, furthermore, export the query as <a href="https://msdn.microsoft.com/query-bi/m/power-query-m-language-specification">Power Query (M) language</a> shown below.

&nbsp;

<img width="1024" height="362" class="alignnone size-large wp-image-1695" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/210-1024x362.png" />

&nbsp;

Now, we can open Power BI desktop, select Get Data &gt; Blank Query to open a new query. Then select Advanced Editor and paste the contents of the exported file into the query. Notice that by default, the query is asking for data for past 1 day, feel free to update it per the requirement.

&nbsp;

<img width="1024" height="712" class="alignnone size-large wp-image-1705" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/39-1024x712.png" />

Lastly, based on the data imported, we can easily create the metrics chart as we want.

&nbsp;

<img width="1024" height="587" class="alignnone size-large wp-image-1755" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/73-1024x587.png" />

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>How to Call Azure Stack API via Client Credential Grant</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/how-to-call-azure-stack-api-via-client-credential-grant</link>
      <pubDate>Tue, 02 Oct 2018 16:22:52 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1655</guid>
      <description><![CDATA[In order to manipulate Azure stack resource by programming, we can call the Azure Stack API to make...]]></description>
      <content:encoded><![CDATA[In order to manipulate Azure stack resource by programming, we can call the Azure Stack API to make it. As both of Azure and Azure Stack share the same <a href="https://docs.microsoft.com/en-us/rest/api/?view=Azure">API surface</a>, if you have been working on API programming in Azure before, it will be very easy to make your current application support azure stack. What needs to be done is just to authenticate to the Microsoft Azure login endpoint. The endpoint will return a token to use in the header of every request sent to the Azure Stack API.
The Microsoft Azure login endpoint support several different OAuth 2.0 grant, I will use client credential here as it doesn't need expose an account password and very suitable for an automation scenario. Before the below process, we need make sure the azure stack has been deployed using Azure AD as the identity store. Also, the tenant user should have created his own subscription from tenant portal.

<strong>Create Service Principal in Azure AD</strong>

Firstly, we need create an application (service principal) in Azure AD that represents your application.
<ol>
 	<li>Sign in to the Azure portal to which the Azure stack is registered.</li>
 	<li>Select Azure Active Directory &gt; App registrations &gt; New application registration.</li>
 	<li>Provide a name and URL for the application. Select either Web app / API or Native for the type of application you want to create. After setting the values, select Create.</li>
</ol>
We should be able to get the application id shown below and also the key in Keys tab. Save both of the application id and key, we will use them later.
<img src="https://msdnshared.blob.core.windows.net/media/2018/10/10-2-2018-1-1024x409.png" alt="" width="1024" height="409" class="alignnone size-large wp-image-1675" />
<br />
<strong>Assign Contributor Role to Service Principal</strong>
<br />
Currently, the service principal doesn't have any permission to the subscription in Azure stack, we need assign it as contributor role for to the subscription, certainly you can assign owner or reader as well.
<ol>
<li>In the Azure stack tenant portal, navigate to the subscription page, select Access Control(IAM) tab.</li>
<li>Select Add, select contributor role, select the application which is just created above. If the application is not listed, use the application id to search shown below.</li>
</ol>
<img src="https://msdnshared.blob.core.windows.net/media/2018/10/10-2--1024x288.png" alt="" width="1024" height="288" class="alignnone size-large wp-image-1665" />   
<br />
<strong>Retrieve Token from Azure</strong>
<br />
Firstly, we need access Azure Stack management endpoint https://management.{region}.{Azure Stack domain}/metadata/endpoints?api-version=2015-01-01 to figure out the azure stack's management resource url. 
The audiences section in the response is the desired resource url. Notice that the url is for tenant user, if you want to perform cloud admin operation mentioned in https://docs.microsoft.com/en-us/rest/api/azure-stack/, please access https://adminmanagement.{region}.{Azure Stack domain}/metadata/endpoints?api-version=2015-01-011
Next, we will create a request body formatted using the content type x-www-form-urlencoded shown below to obtain an access token. POST your request to the Azure REST Authentication and Login endpoint https://login.microsoftonline.com/{tenant id}/oauth2/token. The tenant id can be tenant domain as well.
<pre>
grant_type=client_credentials
&amp;client_id=&lt;the application id retrieved in the first step&gt;
&amp;client_secret=&lt;the application key retrieved in the first step&gt;
&amp;resource=&lt;the resource url just retrieved&gt;
&amp;scope=openid
</pre>
The access_token section in the response is the expected token which will be used in the later API call.
<br />
<strong>Call Azure Stack API</strong>
<br />
Once you get your access token, you need to add it as a header to each of your API requests. In order to do so, you need to create a header authorization with value: Bearer &lt;access token&gt;. For more information about all the available APIs, see <a href="https://docs.microsoft.com/en-us/rest/api/?view=Azure">Azure REST API Reference</a>.
<br />
<strong>Reference</strong>
<br />
https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow
https://docs.microsoft.com/en-us/azure/azure-stack/user/azure-stack-rest-api-use
https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-create-service-principals]]></content:encoded>
    </item>
    <item>
      <title>How to Control Downstream Device from IoT Edge</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/how-to-control-downstream-device-from-iot-edge</link>
      <pubDate>Sat, 08 Sep 2018 00:56:08 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1635</guid>
      <description><![CDATA[For an IoT Edge device which is deployed in manufacture scenario, it is a common requirement to...]]></description>
      <content:encoded><![CDATA[For an IoT Edge device which is deployed in manufacture scenario, it is a common requirement to control the downstream device from a module inside the edge device based on machine's status. This article is about how to control the downstream device from a module based on the below prototype.

<img src="https://msdnshared.blob.core.windows.net/media/2018/09/9-7-1024x298.png" alt="" width="1024" height="298" class="alignnone size-large wp-image-1645" />
<br />
<strong>Downstream Device</strong>

Currently, direct method is the only way to send message to downstream device from a module. In order to support direct method, we need set a callback for the direct call shown below(make sure Microsoft.Azure.Devices.Client 1.18.0 or above is referenced in project file if you are using .net sdk). You may also refer to https://github.com/Azure-Samples/azure-iot-samples-csharp/blob/master/iot-hub/Quickstarts/simulated-device-2/SimulatedDevice.cs for a more details.
<pre>
        private static Task ResetCallback(MethodRequest methodRequest, object userContext)
        {
            var data = Encoding.UTF8.GetString(methodRequest.Data);
            Console.Write(data);
            return Task.FromResult(new MethodResponse(200));
        }
        private static void Main(string[] args)
        {
            Console.WriteLine("IoT Hub Quickstarts #1 - Simulated device. Ctrl-C to exit.\n");

            // Connect to the IoT hub using the MQTT protocol
            s_deviceClient = DeviceClient.CreateFromConnectionString(s_connectionString, TransportType.Amqp);
            s_deviceClient.SetMethodHandlerAsync("reset", ResetCallback, null).Wait();
            while (true)
            {
                // other job        
            }

            Console.ReadLine();
        }
</pre>
<br />
<strong>Configure ASA Job</strong>

Suppose we have configured a streaming analysis job to analyze incoming telemetry, we can flush expected control command into output endpoint shown below, each output record will be a json text like [{"command":"reset}]. Refer to https://docs.microsoft.com/en-us/azure/iot-edge/tutorial-deploy-stream-analytics for more details about how to configure an asa job.
<pre>
SELECT  
    'reset' AS command 
INTO 
   alert 
FROM 
   temperature TIMESTAMP BY timeCreated 
GROUP BY TumblingWindow(second,30) 
HAVING Avg(machine.temperature) &gt; 70
</pre>

<br />

<strong>Custom Module</strong>
Firstly, make sure azureiotedge-agent and azureiotedge-hub have been upgraded to 1.0.1 or above.
If you are using .net SDK to build the custom module, make sure Microsoft.Azure.Devices.Client 1.18.0 or above is referenced in project file, also please use dotnet 2.1 for the docker image shown below:
<pre>
FROM microsoft/dotnet:2.1-sdk AS build-env
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c Release -o out

FROM microsoft/dotnet:2.1-runtime
WORKDIR /app
COPY --from=build-env /app/out ./

RUN useradd -ms /bin/bash moduleuser
USER moduleuser

ENTRYPOINT ["dotnet", "mycustommodule.dll"]
</pre>
The PipeMessage function accepts a message argument, we can get the downstream device id by calling message.ConnectionDeviceId. Then, we can call InvokeMethodAsync shown below to send the reset command.
<pre>
moduleClient.InvokeMethodAsync(message.ConnectionDeviceId, new MethodRequest("reset", Encoding.ASCII.GetBytes(res)));
</pre>
</br>

<strong>Routing</strong>
After configuring the message routing shown below, the downstream device will be able to receive direct method call from the module.
<pre>
{
  "routes": {
    "downstreamdevice2asa": "FROM /messages/* WHERE NOT IS_DEFINED($connectionModuleId) INTO BrokeredEndpoint(\"/modules/asajob/inputs/temperature\")",
    "asa2custommodule": "FROM /messages/modules/asajob/outputs/* INTO BrokeredEndpoint(\"/modules/custommodule/inputs/input1\")",
    "ToIoTHub": "FROM /messages/modules/custommodule/outputs/output1 INTO $upstream"
   }
}
</pre>
]]></content:encoded>
    </item>
    <item>
      <title>How to Deploy Marketplace VMs in Azure Stack by ARM template</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/how-to-deploy-marketplace-vms-in-azure-stack-by-arm-template</link>
      <pubDate>Sun, 05 Aug 2018 00:21:45 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1595</guid>
      <description><![CDATA[To build modern applications across cloud and on-premises environments, Azure and Azure Stack can...]]></description>
      <content:encoded><![CDATA[To build modern applications across cloud and on-premises environments, Azure and Azure Stack can deliver a consistent hybrid cloud platform. Similar to Azure, Azure Stack supports same PowerShell commands, portal experience and similar functionalities. For example, an ARM template which can be deployed in Azure could be deployed to Azure stack without any modification.

In order to deploy Marketplace VMs in Azure Stack by ARM template, we need figure out the corresponding publisher, offer and sku. The easiest way for this is to list all the available images shown below.

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/8-4-2018-1024x538.png" alt="" width="1024" height="538" class="alignnone size-large wp-image-1605" />

Alternatively, we can leverage our existing Azure experience to use Powershell command Get-AzureRmVMImagePublisher, Get-AzureRmVMImageOffer and Get-AzureRmVMImageSku to list all the available publishers, offers and skus. Before that, we need login to Azure Stack, the following is the code for ADFS deployment scenario. Please refer to <a href="https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-powershell-configure-quickstart">Get up and running with PowerShell in Azure Stack</a> for more details. 

<pre>
Import-Module .\Connect\AzureStack.Connect.psm1

# For Azure Stack development kit, this value is set to https://adminmanagement.local.azurestack.external. To get this value for Azure Stack integrated systems, contact your service provider.
$ArmEndpoint = "https://adminmanagement.local.azurestack.external"

# Register an AzureRM environment that targets your Azure Stack instance
Add-AzureRMEnvironment `
    -Name "AzureStackAdmin" `
    -ArmEndpoint $ArmEndpoint

# Get the Active Directory tenantId that is used to deploy Azure Stack     
$TenantID = Get-AzsDirectoryTenantId `
    -ADFS `
    -EnvironmentName "AzureStackAdmin"

# Sign in to your environment
Add-AzureRmAccount `
    -EnvironmentName "AzureStackAdmin" `
    -TenantId $TenantID
</pre>
Lastly, we just need to modify the ARM template's storageProfile node, use the imageReference to configure the Markertplace VM shown below. The GitHub repository https://github.com/Azure/AzureStack-QuickStart-Templates/ has contained more templates for reference.
<pre>
"storageProfile": {
          "imageReference": {
            "publisher": "[parameters('imagePublisher')]",
            "offer": "[parameters('imageOffer')]",
            "sku": "[parameters('imageSku')]",
            "version": "latest"
          },
          "osDisk": {
            "name": "osdisk",
            "vhd": {
              "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob, variables('vmStorageAccountContainerName'),'/', variables('OSDiskName'), '.vhd')]"
            },
            "caching": "ReadWrite",
            "createOption": "FromImage"
          }
        },
</pre>]]></content:encoded>
    </item>
    <item>
      <title>Bring Custom Vision into Azure IoT Edge</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/bring-custom-vision-into-azure-iot-edge</link>
      <pubDate>Fri, 25 May 2018 21:58:55 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1545</guid>
      <description><![CDATA[The Custom Vision Service is a Microsoft Cognitive Service that lets you build custom image...]]></description>
      <content:encoded><![CDATA[The Custom Vision Service is a Microsoft Cognitive Service that lets you build custom image classifiers. It makes it very easy and fast to build, deploy, and improve an image classifier. Since May 7, we are able to export tensorflow model to Dockerfile in custom vision service, that means we can download the artifacts to build our own windows or Linux containers, and furthermore, deploy it to Iot Edge. Notice that Custom Vision Service only support export in compact domains. The models generated by compact domains are optimized for the constraints of real-time classification on mobile devices and classifiers built with a compact domain may be slightly less accurate than a standard domain with the same amount of training data.

After uploading the images and finishing the training, we can find the Export button in PERFORMANCE page and make sure export as DockerFile. The exported artifact is actually a fully functional http server which is listening on 80 port. In order to use it in Iot Edge, the easiest way is keep it as a http server and call the web service from other custom module shown below.<br />
<img src="https://msdnshared.blob.core.windows.net/media/2018/05/325-1024x495.png" alt="" width="1024" height="495" class="alignnone size-large wp-image-1575" />

Any module deployed by edge agent will be located in azure-iot-edge network shown below, that means you don't need to make any port mapping explicitly and the http service can be accessed by other module via https://[module name]:[port number] directly.
<br />
<img src="https://msdnshared.blob.core.windows.net/media/2018/09/Capture-1024x145.png" alt="" width="1024" height="145" class="alignnone size-large wp-image-1615" />

In order to create a custom module to call the AI module, we can make it either by <a href="https://docs.microsoft.com/en-us/azure/iot-edge/tutorial-python-module">cookiecutter</a>, or from visual studio code. Make sure "Azure Iot Toolkit" has been installed to Visual studio code, we will be able to "New IoT Edge Solution" via Command Palette shown below.<br />
<img src="https://msdnshared.blob.core.windows.net/media/2018/05/232-1024x230.png" alt="" width="1024" height="230" class="alignnone size-large wp-image-1565" />
<br />
Certainly, we can modify the exported AI module to a custom module as well, in that case the module will need take care of message input and output as well.
<br />
<img src="https://msdnshared.blob.core.windows.net/media/2018/05/423-1024x316.png" alt="" width="1024" height="316" class="alignnone size-large wp-image-1585" />

On the other hand, I also see some scenario that it is required to access the http service from non-container. It is easy to make port mapping in "Container Create Options" shown below.
<br />
<img src="https://msdnshared.blob.core.windows.net/media/2018/05/140-1024x745.png" alt="" width="1024" height="745" class="alignnone size-large wp-image-1555" />

The below code has exported the http server on port 8085 and the edge host will be able to access it via port 8085. For all the other available options ,please refer to https://docs.docker.com/engine/api/v1.30/#operation/ContainerCreate
<pre>
{
  "HostConfig": {
    "PortBindings": {
      "80/tcp": [
        {
          "HostPort": "8085"
        }
      ]
    }
  }
}
</pre>






]]></content:encoded>
    </item>
    <item>
      <title>Support HTTPS in Azure Marketplace image for Jenkins</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/support-https-in-azure-marketplace-image-for-jenkins</link>
      <pubDate>Mon, 09 Apr 2018 00:17:11 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1515</guid>
      <description><![CDATA[Create a Jenkins server on an Azure Linux VM from the Azure portal has introduced how to configure...]]></description>
      <content:encoded><![CDATA[<a href="https://docs.microsoft.com/en-us/azure/jenkins/install-jenkins-solution-template">Create a Jenkins server on an Azure Linux VM from the Azure portal</a> has introduced how to configure Jenkins server in azure based on <a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/azure-oss.jenkins?tab=Overview">Azure Marketplace image for Jenkins</a>. This image is published by Microsoft, as it has configured Nginx and Jenkins environment well and also make sure they are started automatically,it is very fast and easy to setup the Jenkins based on this image. Based on current implementation, the Jenkins console is inaccessible through unsecured HTTP, there is instruction shown below when accessing the HTTP endpoint. That means in order to secure the conversion, ssh is needed to setup the tunnel.
<img src="https://msdnshared.blob.core.windows.net/media/2018/04/jenkins-ssh-instructions.png" alt="" width="812" height="408" class="alignnone size-full wp-image-1525" />
Recently, I got some questions regarding to how to access Jenkins portal via HTTPS, and this article is about this requirement.
<h3>Generate Let's Encrypt Certificate</h3>
In order to setup HTTPS, we need either create self-signed certificate or require certificate from certificate authority. We will require certificate from Let's Encrypt. Before moving forward, make sure we have already had our own domain name and also make sure a CNAME entry is created for the target VM which should have been provisioned in Azure. Then ssh into the VM and execute the below commands. The first git command is used to clone the letsencrypt GibHub repository into the VM locally, as the image has installed git already, there is no need to install git anymore. When executing letsencrypt-auto command, there are three options nginx, standalone or Apache to choose, I have tried option 2 (standalone) to generate the certificate successfully, just please make sure to "service nginx stop" firstly, as nginx has been configured to bind to 80 port be default, let's encrypt standalone server will fail to bind to 80 port because of that.
<pre>git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto certonly
</pre>
After filling the target domain name and also email address, finally the certificate will created into /etc/letsencrypt/live/customdomain/, we will use the fullchain.pem and privkey.pem in next step.
<h3>Setup SSL Offloading in Nginx</h3>
Though there are quite a lot of web servers in Linux to support SSL offloading, nginx has been installed in the VM and we will go ahead to achieve our requirement just based on a little bit configuration change. The following is the default configuration in /etc/nginx/sites-available/default. As we can see the url rewrite to Jenkins-on-azure, that's the reason why it is showing the ssh instruction page when accessing the portal via HTTP endpoint.
<pre>
server {
    listen 80;
    server_name jacjenkins.centralus.cloudapp.azure.com;
    error_page 403 /jenkins-on-azure;
    location / {
        proxy_set_header        Host $host:$server_port;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;


        # Fix the “It appears that your reverse proxy set up is broken" error.
        proxy_pass          https://localhost:8080;
        proxy_redirect      https://localhost:8080 https://customname.centralus.cloudapp.azure.com;
        proxy_read_timeout  90;
    }
    location /cli {
        rewrite ^ /jenkins-on-azure permanent;
    }

    location ~ /login* {
        rewrite ^ /jenkins-on-azure permanent;
    }
    location /jenkins-on-azure {
      alias /usr/share/nginx/azure;
    }
}
</pre>
Let's modify the above file to the following, it will let nginx listen on 443 port and use the Let's Encrypt certificate to setup the TLS conversation. Save the modified file, execute "service nginx restart", also make sure 443 port is opened in the NSG inbound rule, we should be able to access the Jenkins portal by HTTPS endpoint now. For more detail about nginx SSL setup, please check <a href="https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/">NGINX SSL Termination</a>.
<pre>
server {
    listen 443 ssl;
    server_name customdomain;
    ssl_certificate /etc/letsencrypt/live/customdomain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/customdomain/privkey.pem;
    location / {
        proxy_set_header        Host $host:$server_port;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;


        # Fix the “It appears that your reverse proxy set up is broken" error.
        proxy_pass          https://localhost:8080;
        proxy_read_timeout  90;
    }
}
</pre>
]]></content:encoded>
    </item>
    <item>
      <title>Extend Microsoft.AspNetCore.Authentication.OAuth to Support Reverse Proxy</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/extend-microsoft-aspnetcore-authentication-oauth-for-reverse-proxy</link>
      <pubDate>Sat, 10 Mar 2018 23:45:19 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1505</guid>
      <description><![CDATA[Recently, one customer said his Asp.net Core application used to work very well in OAuth...]]></description>
      <content:encoded><![CDATA[Recently, one customer said his Asp.net Core application used to work very well in OAuth authentication all the time, but after he placed a reverse proxy in front of the web application, the authentication fails because of invalid callback path.

Normally speaking, a callback path is needed when registering an application to the OAuth server. The application will send a callback path to the OAuth server during the authentication workflow as well, OAuth server will verify the client id, client secret and the call back url to make sure the application is a registered application. 

The following is the snippet code to use Microsoft.AspNetCore.Authentication.OAuth module for OAuth authentication in Asp.net Core application.

<pre>public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthentication(options =&gt;
        {
            options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oauth";
        })
        .AddCookie()
        .<strong>AddOAuth</strong>("oauth", options =&gt;
        {
            options.ClientId = Configuration["ClientId"];
            options.ClientSecret = Configuration["ClientSecret"];
            <strong>options.CallbackPath = new PathString(Configuration["CallbackPage"]);</strong>

            options.AuthorizationEndpoint = Configuration["AuthorizationEndpoint"];
            options.TokenEndpoint = Configuration["TokenEndpoint"];
            options.UserInformationEndpoint = Configuration["UserInformationEndpoint"];

            ...
        });
}</pre>


The options.CallbackPath in the above snippet code is PathString type which only accepts relative path begin with '/'. The Microsoft.AspNetCore.Authentication.OAuth module will call the BuildRedirectUri shown below in <a href="https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs">AuthenticationHandler.cs</a> to build the callback url based on the relative callback path provided in options.CallbackPath.
<pre>
protected string BuildRedirectUri(string targetPath)
=&gt; Request.Scheme + "://" + Request.Host + OriginalPathBase + targetPath;
</pre>
While in a reverse proxy scenario, let's suppose the web application's raw domain name is internal.com, the callback url registered into OAuth Server is different such as contoso.com, the domain name of the reverse proxy. Depending on different proxy, some proxy could pass raw domain (internal.com) in the http request's host name instead of contoso.com to the application. Furthermore, if SSL offloading is enabled, the http request schema will be http though end user could use https. That means, the callback registered in OAuth server is https://contoso.com while the application is sending https://internal.com for verification. 

Luckily, most reverse proxy has provided several header to record original information such as <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host">X-Forwarded-Host</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto">X-Forwarded-Proto</a> or equivalent identifier. In order to support reverse proxy, we can extend Microsoft.AspNetCore.Authentication.OAuth to use X-Forwarded-Host and X-Forwarded-Proto to build the callback path. Lastly, make sure call AddMyOAuth instead of AddOAuth in ConfigureServices.
<pre>
public static class MyOAuthExtensions
{
        public static AuthenticationBuilder AddMyOAuth(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions)
            =&gt; builder.AddOAuth&lt;OAuthOptions, MyOAuthHandler&gt;(authenticationScheme, configureOptions);
}


public class MyOAuthHandler : OAuthHandler where TOptions : OAuthOptions, new()
{
        public MyOAuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        { }

        // Copy from https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            if (string.IsNullOrEmpty(properties.RedirectUri))
            {
                properties.RedirectUri = CurrentUri;
            }

            // OAuth2 10.12 CSRF
            GenerateCorrelationId(properties);

            var authorizationEndpoint = BuildChallengeUrl(properties, BuildRedirectUri(Options.CallbackPath));
            var redirectContext = new RedirectContext(
                Context, Scheme, Options,
                properties, authorizationEndpoint);
            await Events.RedirectToAuthorizationEndpoint(redirectContext);
        }

        protected new string BuildRedirectUri(string targetPath)
        {
            var schema = Request.Headers["X-Forwarded-Proto"].Count &gt; 0 ? Request.Headers["X-Forwarded-Proto"][0] : Request.Scheme;
            var host = = Request.Headers["X-Forwarded-Host"].Count &gt; 0 ? Request.Headers["X-Forwarded-Host"][0] : Request.Host;
            return schema + "://" + host + OriginalPathBase + targetPath;         
        }
}
</pre>]]></content:encoded>
    </item>
    <item>
      <title>How to Make Deployment with Ansible Azure Modules</title>
      <link>https://docs.microsoft.com/archive/blogs/wushuai/how-to-make-deployment-with-ansible-azure-modules</link>
      <pubDate>Mon, 12 Feb 2018 17:14:30 GMT</pubDate>
      <dc:creator><![CDATA[Wu Shuai]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wushuai/?p=1405</guid>
      <description><![CDATA[Ansible is an open-source product that automates cloud provisioning, configuration management, and...]]></description>
      <content:encoded><![CDATA[Ansible is an open-source product that automates cloud provisioning, configuration management, and application deployments. It includes a suite of modules for interacting with Azure Resource Manager, giving us the tools to easily create and orchestrate infrastructure on Azure.

In order to use Ansible, we need a linux on-prem server or a linux jumpbox vm in azure. Then follow <a href="https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ansible-install-configure#create-azure-credentials">Install and configure Ansible</a> to install ansible. Ansible communicates with Azure using a username and password or a service principal. An Azure service principal is a security identity that we can use with apps, services, and automation tools like Ansible. As we can control and define the permissions as to what operations the service principal can perform in Azure, and also revoke the permission as we want anytime, it provides a better security experience than user name password approach. Please refer to <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal">Use portal to create an Azure Active Directory application and service principal that can access resources</a> about how to create the service principal and also <a href="https://docs.ansible.com/ansible/latest/guide_azure.html#authenticating-with-azure">Authenticating with Azure</a> about how to authenticate to Azure in Ansible. Just please remember that we also need add the corresponding app as the target resource group or subscription's contributor shown below, otherwise the service principal doesn't have the permission for deployment.

<img width="1024" height="376" class="alignnone size-large wp-image-1376" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/02/addcontri2-1024x376.png" />

If there is no existing linux server to install Ansible, we can also use Azure Cloud Shell which has just supported Ansible since February which means we don't need install Ansible explicitly in Azure Cloud Shell. Also, if  login to the Cloud Shell as the subscription's admin, there is no need to perform authentication step mentioned in the above either. If you are new to Cloud Shell, please refer to <a href="https://docs.microsoft.com/en-us/azure/cloud-shell/overview">Overview of Azure Cloud Shell</a> firstly.

Now, we can create our playbook and perform the deployment. For example the below configuraiton means we are going to create a CentOS virtual machine with user name and password.
<pre>- name: Create Azure VM
  hosts: localhost
  connection: local
  tasks:
  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: myResourceGroup
      name: myVM
      vm_size: Standard_DS1_v2
      admin_username: jacky
      admin_password: changeit
      image:
        offer: CentOS
        publisher: OpenLogic
        sku: '7.3'
        version: latest
</pre>
The azure_rm_virtualmachine in the above configurtion is just the module to create, update, stop and start a virtual machine. For full list of all the supported modules, please refer to below or <a href="https://docs.microsoft.com/en-us/azure/cloud-shell/overview">Azure Cloud Modules</a>.
<ul>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_acs_module.html" class="reference internal">azure_rm_acs - Manage an Azure Container Service Instance (ACS).</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_availabilityset_module.html" class="reference internal">azure_rm_availabilityset - Manage Azure availability set.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_availabilityset_facts_module.html" class="reference internal">azure_rm_availabilityset_facts - Get availability set facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_deployment_module.html" class="reference internal">azure_rm_deployment - Create or destroy Azure Resource Manager template deployments</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_dnsrecordset_module.html" class="reference internal">azure_rm_dnsrecordset - Create, delete and update DNS record sets and records.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_dnsrecordset_facts_module.html" class="reference internal">azure_rm_dnsrecordset_facts - Get DNS Record Set facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_dnszone_module.html" class="reference internal">azure_rm_dnszone - Manage Azure DNS zones.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_dnszone_facts_module.html" class="reference internal">azure_rm_dnszone_facts - Get DNS zone facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_functionapp_module.html" class="reference internal">azure_rm_functionapp - Manage Azure Function Apps</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_functionapp_facts_module.html" class="reference internal">azure_rm_functionapp_facts - Get Azure Function App facts</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_loadbalancer_module.html" class="reference internal">azure_rm_loadbalancer - Manage Azure load balancers.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_loadbalancer_facts_module.html" class="reference internal">azure_rm_loadbalancer_facts - Get load balancer facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_managed_disk_module.html" class="reference internal">azure_rm_managed_disk - Manage Azure Manage Disks</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_managed_disk_facts_module.html" class="reference internal">azure_rm_managed_disk_facts - Get managed disk facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_networkinterface_module.html" class="reference internal">azure_rm_networkinterface - Manage Azure network interfaces.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_networkinterface_facts_module.html" class="reference internal">azure_rm_networkinterface_facts - Get network interface facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_publicipaddress_module.html" class="reference internal">azure_rm_publicipaddress - Manage Azure Public IP Addresses.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_publicipaddress_facts_module.html" class="reference internal">azure_rm_publicipaddress_facts - Get public IP facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_resourcegroup_module.html" class="reference internal">azure_rm_resourcegroup - Manage Azure resource groups.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_resourcegroup_facts_module.html" class="reference internal">azure_rm_resourcegroup_facts - Get resource group facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_securitygroup_module.html" class="reference internal">azure_rm_securitygroup - Manage Azure network security groups.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_securitygroup_facts_module.html" class="reference internal">azure_rm_securitygroup_facts - Get security group facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_storageaccount_module.html" class="reference internal">azure_rm_storageaccount - Manage Azure storage accounts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_storageaccount_facts_module.html" class="reference internal">azure_rm_storageaccount_facts - Get storage account facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_storageblob_module.html" class="reference internal">azure_rm_storageblob - Manage blob containers and blob objects.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_subnet_module.html" class="reference internal">azure_rm_subnet - Manage Azure subnets.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualmachine_module.html" class="reference internal">azure_rm_virtualmachine - Manage Azure virtual machines.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualmachine_extension_module.html" class="reference internal">azure_rm_virtualmachine_extension - Managed Azure Virtual Machine extension</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualmachine_scaleset_module.html" class="reference internal">azure_rm_virtualmachine_scaleset - Manage Azure virtual machine scale sets.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualmachine_scaleset_facts_module.html" class="reference internal">azure_rm_virtualmachine_scaleset_facts - Get Virtual Machine Scale Set facts</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualmachineimage_facts_module.html" class="reference internal">azure_rm_virtualmachineimage_facts - Get virtual machine image facts.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualnetwork_module.html" class="reference internal">azure_rm_virtualnetwork - Manage Azure virtual networks.</a></li>
 	<li class="toctree-l1"><a href="https://docs.ansible.com/ansible/latest/azure_rm_virtualnetwork_facts_module.html" class="reference internal">azure_rm_virtualnetwork_facts - Get virtual network facts.</a></li>
</ul>
Run command "ansible-playbook azure_create_vm.yml" and wait for a while, the vm will be provisioned soon shown below.

<img width="540" height="311" class="alignnone wp-image-1385" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/02/ansible-play.png" />

Notice that Cloud Shell is backed by Azure Storage's file share, if you have one existing yml playbook and would like to upload it to Cloud Shell, just simply upload the asset in the corresponding storage account shown below. Please refer to <a href="https://docs.microsoft.com/en-us/azure/cloud-shell/persisting-shell-storage#transfer-local-files-to-cloud-shell">Transfer local files to Cloud Shell</a> for more details.

<img width="909" height="347" class="alignnone size-full wp-image-1395" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/02/cloud-shell-storage.png" />]]></content:encoded>
    </item>
  </channel>
</rss>