2016年11月11日金曜日

IoT Hubでデバイスを認証させる方法

IoT Hub SDKを使って、IoT Hubに接続するデバイスを認証させることができます。方式としては
・セキュリティトークン
・X.509証明書
の2パターンになります。

セキュリティトークンを使う場合、セキュリティトークンをデバイス対称キーで署名するか、共有アクセスポリシーキーで署名するか、の2パターンになるのですが、共有アクセスポリシーキーで署名するケース(SASトークン)が多いので、ここでは
・SASトークン
・X.509証明書
の2パターンによる認証方法のコード例を示します。なお、コードはIoT Hubのチュートリアルからの変更点または注意点のみを記載します。

SASトークンによる認証:
以下のコードにてデバイスを登録します。
// RegistryManagerを生成
RegistryManager registryManager =     
    RegistryManager.CreateFromConnectionString("<IoT Hubへの接続文字列>");

// デバイスの登録
var device = new Device ("<デバイスID>");
device = await registryManager.AddDeviceAsync(device);

セキュリティトークンを使用するのがデフォルトの認証方式なので、認証方法を指定する必要はありません。

デバイスからIoT Hubに送信するときには、以下のコードにてSASトークンを生成し、デバイスからデータを送信します。
// SASトークンの生成
var sasToken = new SharedAccessSignatureBuilder()
    {
         KeyName = "<共有アクセスポリシー名>",
         Key = "<共有アクセスポリシーのアクセスキー>",
         Target = "<IoT Hubホスト名>" + "/devices/" + "<デバイスID>",
          TimeToLive = TimeSpan.FromDays(Convert.ToDouble("<有効期間>"))
    }.ToSignature();

// SASトークンを使用して、HTTPプロトコルで接続するDeviceClientインスタンスを作成
var authMethod = new DeviceAuthenticationWithToken("<デバイスID>", sasToken);
var deviceClient 
    = DeviceClient.Create("<IoT Hubホスト名>", authMethod, TransportType.Http1);


X.509証明書による認証:
以下のコードにてデバイスを登録します。デバイス登録時に、X.509証明書の拇印を指定します。
// RegistryManagerを生成
RegistryManager registryManager =     
    RegistryManager.CreateFromConnectionString("<IoT Hubへの接続文字列>");

// デバイスの登録
var device = new Device ("<デバイスID>") {
    Authentication = new AuthenticationMechanism()
    {
         // 拇印の指定
         X509Thumbprint = new X509Thumbprint()
             {
                 PrimaryThumbprint = "<X.509証明書の拇印>"
             }
    }
};
device = await registryManager.AddDeviceAsync(device);


デバイスからIoT Hubに送信するときには、以下のコードにてX.509証明書をロードし、デバイスからデータを送信します。
// X.509証明書を使用して、HTTPプロトコルで接続するDeviceClientインスタンスを作成
var x509Certificate = new X509Certificate2("<X.509証明書へのパス>");
var authMethod 
    = new DeviceAuthenticationWithX509Certificate("<デバイスID>", x509Certificate);
var deviceClient 
    = DeviceClient.Create("<IoT Hubホスト名>", authMethod, TransportType.Http1); 

2016年10月14日金曜日

New-SelfSignedCertificateコマンドレットによる自己証明書の作成

New-SelfSignedCertificateコマンドテッドを使用すると自己証明書が簡単に作れます。これを使うことでSSLやAzure IoT Hubのデバイス署名等のテストが楽になります。ただし、コード署名には使えませんので注意してください。

例えばAzure TestというSubjectの証明書をデフォルトのストアに作成する場合は、以下のコマンドを実行します。
New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject "Azure Test"

このコマンドを実行すると証明書が作成され、ThumbprintとSubjectが以下のように出力されます。
Thumbprint                                Subject                                                                             
----------                                -------                                                                             
D9B4240E11048C27F286DFAE3CA11A7833118636  CN=Azure Test 

デフォルトのストアに格納されている証明書を確認する場合は、以下のコマンドを実行します。
Get-ChildItem Cert:\CurrentUser\My

証明書をエクスポートする場合には以下のコマンドを実行します。ここではC:\temp\test.cerにエクスポートしています。Cert引数には先ほど作成した証明書のThumbprintを指定してください。
Export-Certificate -Cert cert:\CurrentUser\my\D9B4240E11048C27F286DFAE3CA11A7833118636 -FilePath C:\temp\test.cer

2016年8月4日木曜日

PowerShellでAzureを操作するときのLocation

PowerShellでAzureを操作するときのLocationですが、いつも使う以外のLocationは忘れがちなので取得方法を書いておきます。

MSDNにはGet-AzureRmLocationコマンドレッドについての記述がありますが、2016年8月現在で使用できないようです。ですので、Get-AzureRmResourceProviderコマンドレッドを使いましょう。
(Get-AzureRmResourceProvider).Locations | Sort-Object | Get-Unique -AsString

結果は以下のようにでます。
Australia East
Australia Southeast
Brazil South
Canada Central
Canada East
Central India
Central US
Central US (Stage)
East Asia
East Asia (Stage)
East US
East US 2
East US 2 (Stage)
Global
global
Japan East
Japan West
MSFT East Asia
MSFT East US
MSFT North Europe
MSFT West US
North Central US
North Central US (Stage)
North Europe
South Central US
South India
Southeast Asia
West Central US
West Europe
West India
West US
West US 2

ちょっと余計なものも出ているような気がしますが。

2016年7月6日水曜日

Microsoft Translatorを利用した日本語→英語翻訳サンプルプログラム

Microsoft Translatorを使う準備
1.Microsoftアカウントを作成します。これはMicrosoft Azure Marketplaceにサインインするために必要になります。
2.Microsoft Azure Marketplace(以下のURL)でサブスクリプション登録を行います。2,000,000文字/月までのデータであれば無償で使えます。
https://datamarket.azure.com/dataset/1899a118-d202-492c-aa16-ba21c33c06cb
3.以下のページからアプリケーションを登録し、「クライアントID」と「顧客の秘密」を取得します。アプリケーションは未作成で問題ありません。
https://datamarket.azure.com/developer/applications/

サンプルプログラムは以下になります。なお、CallMSTranslatorクラスのfromLangとtoLang変数を変更することで、別の言語間の翻訳が可能です。
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using System.Runtime.Serialization.Json;

namespace MSTranslatorApp
{
    class Program
    {
        //MS Translationへの接続情報
        // clientIdには、事前に登録した「クライアントID」を設定
        // clientSecretには、事前に登録した「顧客の秘密」を設定
        private const string CLIENTID = "<クライアントID>";
        private const string CLIENTSECRET = "<顧客の秘密>";

        static void Main(string[] args)
        {
            // 翻訳前のテキストをコマンドライン引数から取得
            string originalText = args[0];

            CallMSTranslator callMSTranslator = new CallMSTranslator(CLIENTID, CLIENTSECRET);

            // 翻訳語のテキストを取得
            string translatedText = callMSTranslator.TranslateMethod(originalText);

            Console.WriteLine("元のテキスト:" + originalText);
            Console.WriteLine("翻訳テキスト:" + translatedText);
            Console.WriteLine();
            Console.WriteLine("何かキーを押してください...");
            Console.ReadKey();
        }
    }

    class CallMSTranslator
    {
        private string authToken;
        private string clientId;
        private string clientSecret;
        private string baseUrl = "http://api.microsofttranslator.com/v2/Http.svc/Translate?text=";

        private string fromLang = "ja";
        private string toLang = "en";
        private string translatedText;

        public CallMSTranslator(string clientId, string clientSecret)
        {
            // Microsoft Translator APIを呼び出す準備
            this.clientId = clientId;
            this.clientSecret = clientSecret;

            AdmAuthentication admAuth = new AdmAuthentication(clientId, clientSecret);
            AdmAccessToken admToken = admAuth.GetAccessToken();
            authToken = "Bearer " + admToken.access_token;
        }

        public string TranslateMethod(string originalText)
        {
            // Microsoft Translator APIを呼び出して、日本語→英語の翻訳を実行
            string uri = baseUrl + System.Web.HttpUtility.UrlEncode(originalText) + "&from=" + fromLang + "&to=" + toLang;
            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
            httpWebRequest.Headers.Add("Authorization", authToken);
            WebResponse response = null;

            try
            {
                response = httpWebRequest.GetResponse();
                using (Stream stream = response.GetResponseStream())
                {
                    System.Runtime.Serialization.DataContractSerializer dcs = new System.Runtime.Serialization.DataContractSerializer(Type.GetType("System.String"));
                    translatedText = (string)dcs.ReadObject(stream);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                    response = null;
                }
            }

            return translatedText;
        }
    }

    public class AdmAccessToken
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public string expires_in { get; set; }
        public string scope { get; set; }
    }

    public class AdmAuthentication
    {
        private static readonly string DatamarketAccessUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13";
        private string clientId;
        private string clientSecret;
        private string request;
        private AdmAccessToken token;
        private Timer accessTokenRenewer;

        // アクセストークンの再生成間隔(アクセストークンは10分で失効する)
        private const int RefreshTokenDuration = 9;

        public AdmAuthentication(string clientId, string clientSecret)
        {
            this.clientId = clientId;
            this.clientSecret = clientSecret;

            // クライアントIDと顧客の秘密をエンコード
            this.request = string.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=http://api.microsofttranslator.com", HttpUtility.UrlEncode(clientId), HttpUtility.UrlEncode(clientSecret));
            this.token = HttpPost(DatamarketAccessUri, this.request);

            // アクセストークンの再生成
            accessTokenRenewer = new Timer(new TimerCallback(OnTokenExpiredCallback), this, TimeSpan.FromMinutes(RefreshTokenDuration), TimeSpan.FromMilliseconds(-1));
        }

        public AdmAccessToken GetAccessToken()
        {
            return this.token;
        }

        public void RenewAccessToken()
        {
            AdmAccessToken newAccessToken = HttpPost(DatamarketAccessUri, this.request);

            //アクセストークンの入れ替え
            this.token = newAccessToken;
            Console.WriteLine(string.Format("Renewed token for user: {0} is: {1}", this.clientId, this.token.access_token));
        }

        public void OnTokenExpiredCallback(object stateInfo)
        {
            try
            {
                RenewAccessToken();
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("Failed renewing access token. Details: {0}", ex.Message));
            }
            finally
            {
                try
                {
                    accessTokenRenewer.Change(TimeSpan.FromMinutes(RefreshTokenDuration), TimeSpan.FromMilliseconds(-1));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("Failed to reschedule the timer to renew access token. Details: {0}", ex.Message));
                }
            }
        }
        public AdmAccessToken HttpPost(string DatamarketAccessUri, string requestDetails)
        {
            // OAuthリクエストの準備 
            WebRequest webRequest = WebRequest.Create(DatamarketAccessUri);
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "POST";
            byte[] bytes = Encoding.ASCII.GetBytes(requestDetails);
            webRequest.ContentLength = bytes.Length;

            using (Stream outputStream = webRequest.GetRequestStream())
            {
                outputStream.Write(bytes, 0, bytes.Length);
            }

            using (WebResponse webResponse = webRequest.GetResponse())
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AdmAccessToken));
                AdmAccessToken token = (AdmAccessToken)serializer.ReadObject(webResponse.GetResponseStream());
                return token;
            }
        }
    }
}

2016年7月4日月曜日

Polybaseを使用して、Blob StorageからSQL DWへCSVファイル形式のデータをロードする方法

Polybaseを使用して、Blob Storageに格納されているCSVファイル形式のデータをSQL DWにロードする手順です。
基本は以下のドキュメントに従って実行しています。
https://azure.microsoft.com/ja-jp/documentation/articles/sql-data-warehouse-load-from-azure-blob-storage-with-polybase/

なお、今回の手順ではファイル名は「simple_benchmark.csv」、データ形式は「TestId, TextNumber」で、ともに整数データとしています。
また、CSVファイルはストレージアカウント「testdatastorage」、コンテナ「testdata」に格納しています。

1.資格情報を作成
CREATE MASTER KEY;

CREATE DATABASE SCOPED CREDENTIAL AzureStorageCredential
  WITH
    IDENTITY = 'testdatastorage',
    SECRET = '<アクセスキー>';

2.外部データソースの作成
CREATE EXTERNAL DATA SOURCE AzureExtStorage
  WITH (
    TYPE = HADOOP,
    LOCATION = 'wasbs://testdata@testdatastorage.blob.core.windows.net',
    CREDENTIAL = AzureStorageCredential
  );

3.データ形式の構成
CREATE EXTERNAL FILE FORMAT CSVFileFormat
  WITH (
    FORMAT_TYPE = DELIMITEDTEXT,
    FORMAT_OPTIONS (
      FIELD_TERMINATOR = ','
    )
  );

4.データベースのスキーマを作成
CREATE SCHEMA [tst];

5.外部テーブルを作成
CREATE EXTERNAL TABLE [tst].[SimbleExtTab1] 
  (
    [TestId] [int] NOT NULL,
    [TestNumber] [int] NULL
  )
  WITH (
    LOCATION = '/simple_benchmark.csv',
    DATA_SOURCE = AzureExtStorage,
    FILE_FORMAT = CSVFileFormat
  );

6.データをSQL DWにロード
CREATE TABLE [tst].[SimpleTab1]
  WITH (
    DISTRIBUTION = HASH([TestId])
  )
  AS
    SELECT * FROM [tst].[SimbleExtTab1];

7.列ストア圧縮の最適化
ALTER INDEX ALL ON [tst].[SimpleTab1] REBUILD;

8.統計の最適化
CREATE STATISTICS [stat_tst_SimpleTab1_TestId] ON [tst].[SimpleTab1]([TestId]);
CREATE STATISTICS [stat_tst_SimpleTab1_TestNumber] ON [tst].[SimpleTab1]([TestNumber]);

2016年6月20日月曜日

Azure Active Directoryのレポート反映時間

Azure Active Directoryのレポートになかなか表示がされないと思っていたら、タイムラグがあったようです。

「Azure Active Directory レポートの待機時間」というタイトルでドキュメントがありましたので、リンクをはっておきます。
https://azure.microsoft.com/ja-jp/documentation/articles/active-directory-reporting-latencies

2016年6月15日水曜日

Azure SQL Databaseの互換性レベルの変更方法

2016年6月中旬より、Azure SQL Databaseでは新規作成したデータベースの既定の互換性レベルが120から130に変更されます。これ以前に作成した既存のデータベースへの影響はなく、各データベースの互換性レベル(100、110、120のいずれか)が維持されます。

このため、SQL Server 2016のクエリプロセッサの機能強化のメリットを得るために、Azure SQL Databaseの互換性レベルを変更することが推奨されています。

互換性レベルは、以下のSQLで変更できます。
ALTER DATABASE <データベース名> SET COMPATIBILITY_LEVEL = 130

なお、互換性レベルは以下の通りです。
  • SQL Server 2008 + SQL Database v11: 100
  • SQL Server 2012: 110
  • SQL Server 2014 + SQL Database v12: 120
  • SQL Server 2016 + SQL Database v12: 130

2016年6月2日木曜日

Azure Machine Learningのモジュール名変更

気が付いたら、Azure Machine Learningのいくつかのモジュール名が変更になっていました。
  • Project Columns -> Select Columns in Dataset
  • Quantize Data -> Group Data into Bins
  • Sweep Parameters -> Tune Model Hyperparameters
  • Metadata Editor -> Edit Metadata
  • Descriptive Statistics -> Summarize Data
  • Reader -> Import Data
  • Writer -> Export Data
  • Image Reader -> Import Images

2016年5月18日水曜日

SQL DW May 2016 updateにおけるLarge-row support

SQL DWのMay 2016 updateで行あたりのバイト数が32KBという制限が外れ、SQL Serverと同様になったようです。
ただし、May 2016 updateではPolybaseを使った場合の32KBの制限は残っています。(そのうち外れる様子)

2016年4月8日金曜日

Bot Frameworkベースのアプリ実行に、401 Unauthorizedや403 Forbiddenが出る場合

Bot Frameworkでアプリを作成してデプロイした場合、Basic認証ができずにエラーになることがあります。 これは、Emulatorを使った場合、App Appsとしてデプロイした場合、双方で発生します。

これは、どうやらBot FrameworkがBasic認証をうまくハンドリングできていないのが原因のようです。

回避策としては、
1. HTTPSで接続できるようにする
2. Basic認証を外す
となるのですが、お試しでやるときにHTTPSを構成するまでもないと思うので、Basic認証の外し方を記述しておきます。

やり方は単純で、MessageController.cs内の[BotAuthentication]属性をコメントアウトするだけです。
namespace Bot_Application1
{
    //[BotAuthentication] //この属性をコメントアウトする
    public class MessagesController : ApiController
    {
     ......

2016年3月18日金曜日

HDInsightクラスタ(Windows)でヘッドノードに管理者権限を付与できない件について

HDInsightクラスタに紐づいたストレージ間でAZCopyでデータコピーをしようという要件がありそうなのですが、これは第3者のVMを経由しないとだめなようです。
AZCopyをインストールするにはWindowsの管理者権限が必要なのですが、これが不可とのことです。

参考:Administrator Privileges on Headnode of HDInsight-Cluster

2016年3月14日月曜日

Azure SQL DatabaseでSQLの実行時間を図る方法

SQL文の実行時間を測定したい場合は
set statistics time on
をSQL文の前に実行します。
例えば
set statistics time on;
go
select * from saleslt.Customer;
go
といったSQL文を実行すると
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.

    (847 row(s) affected)    

 SQL Server Execution Times:
   CPU time = 16 ms,  elapsed time = 8 ms.
といった実行時間の情報が得られます。

また、平均CPUに基づいて上位N位のSQL文を取得したい場合には、動的管理ビュー「sys.dm_exec_query_stats」を使います。
例えば上位5位までのSQL文を取得したい場合には
SELECT TOP 5 query_stats.query_hash AS "Query Hash", 
    SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS "Avg CPU Time",
    MIN(query_stats.statement_text) AS "Statement Text"
FROM 
    (SELECT QS.*, 
    SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1,
    ((CASE statement_end_offset 
        WHEN -1 THEN DATALENGTH(st.text)
        ELSE QS.statement_end_offset END 
            - QS.statement_start_offset)/2) + 1) AS statement_text
     FROM sys.dm_exec_query_stats AS QS
     CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as query_stats
GROUP BY query_stats.query_hash
ORDER BY 2 DESC;
GO
といったSQL文を実行します。

なお、残念ながらこれらはAzure SQL Datawarehouseでは使用できないようです。

参考:https://technet.microsoft.com/ja-jp/library/gg457940.aspx

2016年2月5日金曜日

リモートデスクトップの接続数制限解除

AzureでSQL Database等を操作するときに、踏み台用のWindows VMを構成し、リモートデスクトップ経由で操作することがあるかと思います。 Windows Server 2012 R2のリモートデスクトップ接続はデフォルトだと1セッションしか接続出来ないので、この制限を解除する手順を紹介します。

  1. 「ファイル名を指定して実行」より「gpedit.msc」と入力
  2. 「ローカルコンピューターポリシー」->「コンピューターの構成」->「管理用テンプレート」->「Windowsコンポーネント」->「リモートデスクトップサービス」->「リモートデスクトップセッションホスト」->「接続」を選択
  3. 「リモートデスクトップサービスユーザーに対してリモートデスクトップサービスセッションを1つに制限する」を「無効」に変更

これでリモート接続上限2になり、コンソールと合わせると3セッションの操作が可能になります。
同一ユーザでも2セッション張る事が可能です。

2016年1月21日木曜日

SQLサーバーからランダムに行を取り出す方法

SQLサーバーからランダムに行を取り出したいとき、以下のSQLが最もコストが低いようです。例えば、<取り出す割合>を10と指定すると、10%の行をランダムに取り出します。
SELECT * FROM <表名>
WHERE (ABS(CAST((BINARY_CHECKSUM (<キー列>, NEWID())) as int)) % 100) < <取り出す割合>

2016年1月8日金曜日

PowerShellで、仮想マシンを起動停止してみる

Azureポータルにアクセスしなくても、PowerShellで仮想マシンの起動停止が簡単にできます。

仮想マシンを起動するコマンド
Start-AzureRmVM -ResourceGroupName <リソースグループ名> -Name <仮想マシン名>

仮想マシンを停止するコマンド
Stop-AzureRmVM -ResourceGroupName <リソースグループ名> -Name <仮想マシン名>

なお、リソースグループ名と仮想マシン名がわからない場合も、コマンドで取得できます。
# リソースグループ名
Get-AzureRmVM | Select ResourceGroupName

# 仮想マシン名
Get-AzureRmVM | Select Name

ARMテンプレートを使ってWindows仮想マシンを作ってみる

Azure Powershell 1.0以前のバージョンではAzure Service Management(ASM)とAzure Resource Manager(ARM)関連のコマンドレッドの切り替え(Switch-AzureModule)が必要でしたが、1.0からはARM関連のコマンドレッドはAzureRMモジュールに含まれるようになりました。以下では、AzureRMモジュールを使ってWindows仮想マシンを作成します。

仮想マシンをAzureにデプロイする場合の基本構文は、以下になります。
#パラメータの指定
$deployName="<デプロイメント名>"
$rgName="<リソースグループ名>"
$locName="<場所>"
$templateURI="<テンプレートのURI>"

# リソースグループの作成
New-AzureRmResourceGroup –Name $rgName –Location $locName

# 仮想マシンをデプロイ
New-AzureRmResourceGroupDeployment -Name $deployName -ResourceGroupName $rgName -TemplateUri $templateURI

Windows仮想マシンを最も簡易的に作成するテンプレートのURIは
https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/101-vm-simple-windows/azuredeploy.json
※最新のテンプレートについては、GitHub上でURIを確認してください。

これは、Windows Server 2012 R2 Datacenterの仮想マシンをStandard D2サイズで作成するテンプレートになります。
このテンプレートを使用する場合、指定できるパラメータは、ユーザー名、パスワード、DNS名ラベルのみになります。その他のパラメータを変更したい場合は、テンプレートをカスタマイズして使用してください。

なお、テンプレートのREADMEにはWest USロケーションに仮想マシンを作成すると書かれていますが、上記のようにリソースグループでロケーションを指定することにより、West USロケーション以外に仮想マシンを配置することが可能なようです。