2016年8月25日

[Azure]客製化IOT Suite Portal的儀錶板

Azure IOT Suite是一個開源、預先設置好的套件,透過此套件可以讓我們快速的客製化開發IOT的解決方案。關於IOT Suite詳細的資訊可以參考這裡:https://azure.microsoft.com/zh-tw/suites/iot-suite/

在所有的IOT專案中,儀表板幾乎都是需要相當程度客製化的;一般來說,我們可以透過PowerBI來拉好所需的報表,再鑲入IOT Suite的Portal上;或是使用其他第三方廠商或是開源的專案來製作報表(例如Freeboard)

這裡我想展示的是如果我們只是單純地想在IOT Suite介面上增加簡單的欄位,所需的客製化步驟。

在開始進行前,我們需要一個前端模擬器,這個前端模擬器會模擬設備送上數據以及metadata;我已經事先準備好了一個IOT Suite模擬器:https://github.com/michael-chi/Azure-IOTHub/tree/master/device-simulator/iotsuite-simulator/iotsuite-simulator

在這個模擬器中,我們會在Metadata中設定IOT Suite預設的portal儀錶板上Telemetry History圖表上要顯示的欄位;其設定的方式在這裡:https://github.com/michael-chi/Azure-IOTHub/blob/master/device-simulator/iotsuite-simulator/iotsuite-simulator/IOTSuiteClientHelper.cs

此模擬器會產生如下格式的模擬數據:{"DeviceId":"device004","_0101":3,"_0102":0,"_0103":32,"_0104":78,"_0105":0}

而我會在此儀表板上新增一個欄位,用字符的方式將資料展示出來,結果會像下面這樣:

  • 首先,我們依照IOT Suite的架構,在Views/Dashboard下新增一個Partial View (Razor)叫做_KPI.cshtml;程式碼如下,其中名為kpe的Div即是我們要顯示資料的地方。
  • 在程式碼中,我們呼叫IoTApp.Dashboard.KPI.init()將一些初始化資料傳給KPI物件;

@using GlobalResources
    <div>
        <div></div>
        <div type="text" id="kpi" name="kpi" style="font-size:24px"/>
    </div>
    <script src="~/Scripts/Views/Dashboard/KPI.js"></script>
    <script type="text/javascript">
        (function () {
            'use strict';

            var kpiSettings = {
                targetControl: $('#kpi')
            };

            IoTApp.Dashboard.KPI.init(kpiSettings);
        })();
    </script>

  • 接著,要新增KPI物件的定義;在Scripts/Views/Dashboard下新增一個KPI.js

IoTApp.createModule(
    'IoTApp.Dashboard.KPI',
    (function () {

        var targetControl;
   
        'use strict';
        var updateKPI = function (newData, fields) {
           var latest = newData[newData.length - 1]; //最新一筆資料
            $(targetControl).text(latest.values._0101);//將最新一筆資料展示到畫面上
        };
   
        var init = function (settings) {
            targetControl = settings.targetControl;
        };
 

    return {
        updateKPI: updateKPI,
        init: init
    }
}), [jQuery, resources]);

  • 然後將這個KPI控制項展示到畫面上
  • 在_DashboardDevicePane.cshtml中,加入下面的程式

<div class="telemetryhistory">
    @{
        Html.RenderPartial("_TelemetryHistory");
        Html.RenderPartial("_TelemetryHistorySummary");
        Html.RenderPartial("_KPI");
    }
    <div id="loadingElement" class="loader_container loader_container_details">
        <div class="loader_container__loader loader_container__loader--large_top_margin"></div>
    </div>

  • 把控制項畫到畫面上之後,當然我們必須要告訴他的parent control,也就是DashboardDevicePane我們多加了一個控制項,當畫面更新時要一起更新他
  • 在Scripts\Views\Dashboard\DashboardDevicePane.js新增下面程式碼:

  • 在DashboardDevicePane的refreshData方法中,呼叫kpiRefreshData()來更新KPI控制項

  • 完成之後佈署到Azure上,啟動前端模擬器就可以看到結果了!

完整的範例程式碼在此:https://github.com/michael-chi/azure-iot-suite-customization-demo

2016年8月16日

用Azure Alert與Azure Automation自動回應系統警告

Azure Alert可以讓我們針對系統的效能指標設定警告,例如當CPU五分鐘內平均用量大於90時,發出一個警告;這時我們通常會希望系統自動將機器提升到更高等級。或是例如當Azure Service Bus Queue用量到達一定程度時,發出警告郵件給管理者等等。

這類自動針對警告做出相對動作的機制,在Azure上可以透過Azure Automation的WebHook來達成;以下我會以Azure SQL Database作為範例,展示當Azure SQL Database的Storage用量到達一定程度時,透過Automation Webhook自動調升SQL等級。

當然,上述功能也可以透過Azure SQL Database的Elastic Database Pool讓我們更彈性的使用保留的DTU,特別是當我們有多個資料庫時,Elastic Database Pool可以更有效的運用資源並節省成本。但當我們只有一個SQL Database時,透過Automation + Alert可能更為節省資源成本。

  • 首先,我們先建立一個Azure Automation Account;在建立時,我們要順便建立一個Service Principle,這個Service Principle可以提供稍後我們所需要的Azure連線身分

  • 建立完成之後,會產生兩個Connection Asset,分別對應到Classic與AAD模式的連線方式。稍後我們在Run book中會用它來連接到我們的Azure訂閱

$servicePrincipalConnection=Get-AutomationConnection -Name "AzureRunAsConnection" 
Add-AzureRmAccount `
    -ServicePrincipal `
    -TenantId $servicePrincipalConnection.TenantId `
    -ApplicationId $servicePrincipalConnection.ApplicationId `
    -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint

  • 接著,建立一個WebHook

  • 將這串位址記錄下來並完成建立

  • 接著,回到Azure SQL Database的設定頁面,建立一個Alert規則

  • 設定完規則定義之後,將剛剛紀錄的WebHook位址貼上來;這樣每次規則被Trigger時,系統便會呼叫這個API並將Alert資料傳進去。

  • 記得啟用規則;接著,當設定的條件達到時,就可以看到Automation Runbook被呼叫執行了

2016年8月5日

[Azure]存取AD B2C tenant的資料

在上兩篇文章中,我們示範了如何建立Azure B2C Tenant以及如何透過B2C Tenant驗證管理會員資料;接下來我們要模擬一個情境:我的公司管理網站需要透過企業AD驗證身分,並且我作為一個管理員可以透過此管理網站取得(或修改)已經登入的會員資訊。

為了達到這個模擬情境,我們需要準備以下的資源:

  • 一個Azure AD Tenant(非B2C Tenant)
  • 一個Azure AD B2C tenant
  • 一個管理網站,並設定期使用Azure AD驗證身分

為了方便起見,這裡的Azure AD Tenant我使用公司本身的Azure AD;Azure B2C Tenant則沿用上篇文章中所使用的B2C Tenant。

在B2C Tenant建立App

  • 輸入基本資料

  • 完成

  • 接著,我們要給予這個App存取B2C Tenant中AD資料的權限;回到管理介面,打開此App的設定頁面

  • 在這裡,因為範例的緣故,我給予這個app完整的AD存取權限;實際上的權限可以依需要指定

  • 接著記下Client ID與Client Secret;這裡Client Secret必須等我們儲存所有設定之後才會顯示

  • 確認所有資料無誤之後按下儲存。
  • 我把存取B2C Tenant所需要的相關API,包成一個B2C Client Library;為了展示的目的,這個Library中現在只包含一個GetUsers()的方法,此方法會取得所有B2C Tenant中的使用者資料
  • 開啟Visual Studio,建立一個新的Class Libaray專案,並加入Microsoft.IdentityModel.Clients.ActiveDirectory package (版本2.18.206251556)

B2C Client Library

  • 程式碼如下;其中Constructor接受三個參數:TenentName, ClientId與ClientSecret
    • TenantName就是B2C Tenant的名稱,例如demo.onmicrosoft.com
    • ClientId與ClientSecret為剛剛記下的ClientID與ClientSecret
  • 完整的Azure AD Graph API文件請參考這裡:https://msdn.microsoft.com/library/azure/ad/graph/api/api-catalog

public class AADB2CClient
{
    //private string TenantId;
    private string ClientId;
    private string ClientSecret;
    private string TenantName;
    private string BearerToken;
    public AADB2CClient(string tenantName,string clientId, string clientSecret)
    {
        ClientId = clientId;
        ClientSecret = clientSecret;
        TenantName = tenantName;
    }
    protected string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;
        var context = new AuthenticationContext($"https://login.microsoftonline.com/{TenantName}");
        var thread = new Thread(() =>
        {
            result = context.AcquireToken("https://graph.windows.net", new ClientCredential(ClientId, ClientSecret));
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        BearerToken = result.AccessToken;
        return BearerToken;
    }
    //https://graph.windows.net/{tenant-name}/users?api-version=1.6
    public string GET(string url)
    {
        string token = GetAuthorizationHeader();
        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
        request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
        request.Method = "GET";
        using (var respStream = request.GetResponse().GetResponseStream())
        {
            using (var sr = new StreamReader(respStream))
            {
                return sr.ReadToEnd();
            }
        }
    }
}

管理介面

  • 管理網站的程式碼較為簡單,為了方便起見,我直接將網站發布到Azure Web App,然後設定為透過公司的AAD驗證

  • 程式的部分僅是在Controller中呼叫B2CClient,並傳入B2C Tenant所需的Tenant Name、Client ID與Client Secret

[Authorize]
public class B2CController : Controller
{
    [HttpGet]
    public ActionResult GetUsers()
    {
        AADB2CClient client = new AADB2CClient(
           tenantName: "mydemob2ctenant.onmicrosoft.com",
           clientId: "{my client id}",
           clientSecret: "{my client secret}");
        var resp = client.GET("https://graph.windows.net/mydemob2ctenant.onmicrosoft.com/users?api-version=1.6");
        B2CModel model = new B2CModel();
        model.Text = resp;

        return View(model);
    }

}

2016年8月1日

[Azure]用Azure AD B2C實現Facebook登入

上一篇我們展示了如何透過Azure AD B2C實現自訂email登入,接著我們要實現Facebook登入。本篇範例使用的展示網站程式碼與上一篇所使用的網站程式碼相同。

首先,我們需要註冊一個Facebook的應用

  • 接著,新增一個產品

  • 新增Facebook登入

  • 記下App ID與Client Secret

  • 保存所有設定
  • 接著,登入https://portal.azure.com ,然後打開Azure AD B2C管理介面(或是由https://manage.windowsazure.com 進入管理介面)
  • 新增一個Identity Prodiver,指定Facebook,將AppID與Client Secret填入

  • 打開上一篇我們所建立的Sign-up or Sign-In原則

  • 把Facebook Identity provider加入

  • 接下來,如果需要針對登入頁面作客制化,可以點選UI Customization,打開UI客製化的Blade

  • 這裡,系統提供了預設的登入頁面;如果需要客製化,可以參考這裡的說明進行客製:https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-reference-ui-customization/
    • 基本上的概念是,我們必須提供我們自己的HTML頁面,這個頁面必須包含某些必要的欄位或設定,並且host在一個可由Internet匿名存取的位置。你也可以使用CDN來加速使用者下載網頁的速度。
    • 然後,將這個客製化頁面的網址填在下面Custom page URI的位置;如此當使用者登入時,系統會由這裡把我們的頁面內容崁入系統的登入頁面上。

  • 在這裡為了簡單,我全部使用預設頁面
  • 設定完成後,儲存設定。
  • 打開上一篇完成的網站,按下Login,可以看到Facebook的登入按鈕。

  • 試著登入看看,可以發現我們被導向了Facebook的登入畫面,並需要遵循Facebook上定義的多因素登入驗證方式(如果有設定)

  • 完成登入後,看看Claim,確實是由Facebook負責驗證

Blog Archive

About Me