normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Windows Azure の Webサイトで Tomcat, Jetty の稼働をサポート!!!

さて、今回はWindows Azure アドベントカレンダー の15日目だが、ここで大きなニュースがある。
来た! Web サイトで Java が動く日がついに来たのだ! さあ皆、↓の画面を見るんだ!!
f:id:waritohutsu:20140315204517j:plain

上記の様に JDK 1.7 update 51 が選択可能で、ついでに Tomcat と Jetty が動くのが確認できる。今回はこのネタを話したいと思う。

Kudu を利用したフォルダ構成、環境変数の確認

次にちょっとフォルダ構成を確認するために、Kudu を利用したいと思う。Kudu を知らない人は、No.1 が詳しい記載のある Windows Azure Web Sitesの魅力を120%引き出す を確認してみよう!

一旦は Tomcat 側で設定した Web サイトを作成し、Web サイトに https://<サイト名>.scm.azurewebsites.net/ でアクセスして、構造を確認してみよう。Kudu の Debug Console を利用するこでフォルダ構成をチェックできる。Tomcat のインストールディレクトリを確認したところ、以下の様に D:\Program Files (x86)\ 以下に配置されている。

D:\Program Files (x86)\apache-tomcat-7.0.50>dir
Volume in drive D is Windows
Volume Serial Number is 6416-721C
Directory of D:\Program Files (x86)\apache-tomcat-7.0.50
03/11/2014 10:20 PM <DIR> .
03/11/2014 10:20 PM <DIR> ..
03/11/2014 10:20 PM <DIR> bin
03/11/2014 10:20 PM <DIR> conf
03/11/2014 10:20 PM <DIR> lib
03/11/2014 10:20 PM <DIR> logs
03/11/2014 10:20 PM <DIR> temp
03/11/2014 10:20 PM <DIR> webapps
03/11/2014 10:20 PM <DIR> work
03/11/2014 10:20 PM 57,862 LICENSE
03/11/2014 10:20 PM 1,228 NOTICE
03/11/2014 10:20 PM 9,258 RELEASE-NOTES
03/11/2014 10:20 PM 16,742 RUNNING.txt
4 File(s) 85,090 bytes
9 Dir(s) 9,324,318,720 bytes free

次に、war を配置する webapps の設定を確認するために conf/server.xml の中身の一部も確認してみる。

D:\Program Files (x86)\apache-tomcat-7.0.50\conf>cat server.xml 

<中略>

<Host name="localhost" appBase="d:\home\site\wwwroot\webapps" xmlBase="d:\home\site\wwwroot\"
unpackWARs="true" autoDeploy="true" workDir="${site.tempdir}">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="${site.logdir}"
prefix="site_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host> 

以下の様に war を配置するフォルダは appBase="d:\home\site\wwwroot\webapps" で記載されている。どうやらここに war を配置すればよいのだと推察できる。次に d:\home\site\wwwroot\webapps を確認すると、ROOT フォルダが存在し、以下に一つだけ index.jsp ファイルが存在している。
f:id:waritohutsu:20140315204906j:plain

ここで http://<サイト名>.azurewebsites.net にアクセスすると以下の画面が表示され、Oracle JDK かつ GMT タイムゾーンであることが確認できる。
f:id:waritohutsu:20140315204947j:plain

次に、環境変数を確認する。ここでは JDK, Jetty, Tomcat 向けに設定された環境変数の情報が確認可能だ。
f:id:waritohutsu:20140315205001j:plain

自前アプリをデプロイ

Kudu さんの Debug Console からドラッグアンドドロップで HelloWorld.war を d:\home\site\wwwroot\webapps 以下に配置した後、https://<サイト名>.azurewebsites.net/HelloWorld にアクセスすると以下の様に自前で作成した HelloWorld.war が正しくデプロイしていることが確認できる。
f:id:waritohutsu:20140315205519j:plain

次に git clone を利用して Web サイトをクローンしたところ、d:\home\site\ から引っこ抜かれてしまったので、毎度 war を push する必要がありそうだ。この辺りは Eclipse からのデプロイ等の改善が期待されるところだ。

C:\xxxxxxxxxxxxx\ilovejava>dir /a
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は D461-5218 です

 C:\xxxxxxxxxxxxx\ilovejava のディレクトリ

2014/03/15  15:15    <DIR>          .
2014/03/15  15:15    <DIR>          ..
2014/03/15  19:28    <DIR>          .git
2014/03/15  15:15            65,954 hostingstart.html
2014/03/15  20:51    <DIR>          webapps

仮想ネットワーク上に作成する仮想マシンに対し、固定 IP アドレスを割り当て可能に

今まで仮想ネットワーク上に作成する仮想マシンIPアドレスは固定化できず、DHCPで割り振られる IP アドレスのみ利用可能でしたが、 PowerShell cmdlets for Windows Azure version 0.7.3 から IP アドレスが固定化できるようになった。
Windows Azure now allows to set fixed IP-addresses for virtual machines

PowerShell のコマンドから以下をたたくと、IPアドレス固定化に利用するためのコマンド(Set-AzureStaticVNetIP)が存在しているのが確認できる。

PS C:\> Get-Command *Azure*

f:id:waritohutsu:20140306005036j:plain

備考

DHCP で動的に割り当てられる IP は「10.x.x.4 ~ の 4から割り当てられる」や DNSサーバーの割り当ては固定化できる等の特性を考慮した工夫である程度対応できたが、PowerShell cmdlets for Windows Azure version 0.7.3 からは、仮想マシンに対して明示的に固定のIPアドレスをふれるようになった。
Set-AzureStaticVNetIP が利用できるようになる前の情報については、神ブログである SE の雑記 に記載されている AzureVM の DNS の設定についての注意点を参照のこと

Web サイトのバックアップ/復元 機能を利用してみる

Azure: ExpressRoute Dedicated Networking, Web Site Backup Restore, Mobile Services .NET support, Hadoop 2.2, and more にて発表された Webサイトのバックアップ/復元機能が便利そうだったのでちょっと触ってた。

まず簡単なWEBアプリを作る

Node.js で簡単にテーブル操作する処理を以下の様に実装した。今回は Node.js アプリ& git デプロイした場合のフォルダ構成となる点だけ認識頂ければ幸いだ。

  • server.js
var http = require('http')
var port = process.env.PORT || 1337;

var azure = require('azure');
var ServiceClient = azure.ServiceClient;
var TableQuery = azure.TableQuery;
var tableClient = azure.createTableService("<ストレージアカウント名>",
 "<ストレージアカウントキー>",
 "http://<ストレージアカウント名>.table.core.windows.net/");

var tableName = 'posts';
var partition = 'part1';
var rowkey = 'bbbbbb';

http.createServer(function(req, res) {
  tableClient.createTableIfNotExists(tableName, function (err, created) {
      var now = new Date().toGMTString();
      var entity = {
          PartitionKey: partition,
          RowKey: rowkey + now,
          title: 'Post one',
          body: 'Body one at WebSite',
          created_at: now };
      tableClient.insertEntity(tableName, entity, null, function(err){
         console.log(err);
      });
  });

  res.writeHead(200, { 'Content-Type': 'text/html; charset=UTF-8' });
  res.end('無事にテーブルストレージにインサートした模様\n');
}).listen(port);

以下のコマンドでモジュールをインストール後、git push してアプリケーションを動作させる。

npm install azure 
git add .
git commit -m "commit"
git push <hogehoge>

バックアップを試す

Webサイトの管理画面から「バックアップ プレビュー」のタブがあるので選択する。「標準モード」でしか利用できないので、注意が必要だ。
f:id:waritohutsu:20140304011118j:plain

自動化されたバックアップを頻度を含め設定できるほか、何処のストレージサービスに格納するかを設定できる。
f:id:waritohutsu:20140304011144j:plain

「今すぐバックアップ」を選択すると選択したストレージサービスにデータが格納される。ストレージサービスに「websitebackups」フォルダが作成され、「_<日付>」フォーマットの xml ファイルと zip ファイルが格納されている。
f:id:waritohutsu:20140304011207j:plain

  • normalianbkup_201403031549.xml
<?xml version="1.0" encoding="utf-8"?>
<BackupDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure">
  <Version>0</Version>
  <Name><Webサイト名>_<日付></Name>
  <HostNames xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d2p1:string><Webサイト名>.azurewebsites.net</d2p1:string>
  </HostNames>
  <Databases />
</BackupDescription>
  • normalianbkup_201403031549.zip ファイルの中身
normalianbkup_201403031549.zip
└─fscontent
   ├─LogFiles
   │  └─Git
   │      └─trace
   └─site
       ├─deployments
       │  ├─9e3f836b7daa0648bb95a9371f3fd748d
       │  └─tools
       ├─repository
       │  └─.git
       └─wwwroot
           └─node_modules

zip ファイル側は site 以下のファイルがまるっと zip に固められていることがわかる。

復元を試す

「今すぐ復元」を選ぶとどのバックアップファイルからWebサイトを復元するか選択可能だ。
f:id:waritohutsu:20140304011637j:plain

また、復元先はもともとのWebサイトはもちろん、新たなWebサイトを作成してバックアップすることも可能だ。
f:id:waritohutsu:20140304011648j:plain

Open JDK ベースの JVM Zulu が Web Platform Installer でインストール可能に!

ご存じでない方もいると思うが、OpenJDK ベースである Azul Zulu が Web Platform でインストール可能になった。Zulu は Windows Azure Plugin for Eclipse with Java にも統合されており、Windows Azure 上で Java を動作させる際に重要な役割を果たしている。

Web Platform Installer を利用してインストール

Web Platform Installer を起動し、検索までから Java と検索することで以下の画面が表示される。
f:id:waritohutsu:20140209143119p:plain

そのまま[追加]ボタンを押下すると以下の様にインストールされる。

  • C:\Azul\zulu1.7.0_45-7.2.0.3-win64 以下に JDK がインストールされる(※注 バージョンによって適宜読み替えること
  • 環境変数 JAVA_HOME が以下に設定される(※注 既存の JDK をインストールしている場合は注意)
C:\Users\normalian>echo %JAVA_HOME%
C:\Azul\zulu1.7.0_45-7.2.0.3-win64

Web Platform Installer を利用することで、常に最新のバージョンの Zulu を利用することが可能だ。

余談

Windows Azure Plugin for Eclipse with Java をインストールしている場合、生成される Windows Azure 向けのプロジェクトテンプレートに含まれる package.xml にも zulu を取得する URL が含まれている。以下に抜粋したものを記載する。

  • package.xml の抜粋
        <!-- Create a sample role -->
        <workerrole approotdir="${basedir}\WorkerRole1\approot" name="WorkerRole1">
          <startupenv cloudvalue="%DEPLOYROOT%\zulu1.7.0_40-7.1.0.0-win64" name="JAVA_HOME" type="jdk.home" value="%DEPLOYROOT%\zulu1.7.0_40-7.1.0.0-win64"/>
          <startupenv name="PATH" type="jdk.path" value="%JAVA_HOME%\bin;%PATH%"/>
          <startupenv name="CATALINA_HOME" type="server.home" value="%DEPLOYROOT%\apache-tomcat-7.0.47"/>
          <startupenv name="SERVER_APPS_LOCATION" type="server.app.loc" value="%CATALINA_HOME%\webapps"/>
          <component cloudaltsrc="http://azure.azulsystems.com/zulu/zulu1.7.0_40-7.1.0.0-win64.zip" cloudmethod="unzip" cloudsrc="auto" cloudupload="AUTO" deploydir="%DEPLOYROOT%" deploymethod="copy" importmethod="copy" importsrc="C:\Program Files\Java\jdk1.7.0_40" type="jdk.deploy"/>
          <component cloudmethod="unzip" cloudsrc="auto" cloudupload="AUTO" deploydir="%DEPLOYROOT%" deploymethod="copy" importmethod="copy" importsrc="C:\opt\tomcat\apache-tomcat-7.0.47" type="server.deploy"/>

Windows 向けの Open JDK で公式ビルドは存在しないが、Zulu という選択肢も取れることがわかる。

Knockout MVC を使ってみる

今回は Knockout.js を ASP.NET MVC で活用するためのライブラリである Knockout MVC を利用してみる。同ライブラリを利用することで、ASP.NET MVC でのモデルバインディングが容易になり、ASP.NET 側と JavaScript 側の機能連携が非常に容易になる。

インストール方法

Visual Studio から NuGet の管理コンソールを起動し、検索窓に「knockout」と入力した結果の P.3 辺りに Knockout MVC が発見できる。
f:id:waritohutsu:20140113134146j:plain

次に、Knockout.js を利用するためには ASP.NET MVC プロジェクトに対して以下の修正をする必要がある。

  • App_Start/BundleConfig
public class BundleConfig
{
    // バンドルの詳細については、http://go.microsoft.com/fwlink/?LinkId=301862 を参照してください
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.validate*"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryunobtrusive").Include(
        "~/Scripts/jquery.validate.unobtrusive.js"));

        //knockout.s 用
        bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
        "~/Scripts/knockout-{version}.js"));
        bundles.Add(new ScriptBundle("~/bundles/knockoutmapping").Include(
        "~/Scripts/knockout.mapping-latest.js"));

        // 以下は省略
    }
}
  • Views/Shared/_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
//中略
</head>
<body>
//中略
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - マイ ASP.NET アプリケーション</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/knockout")
    @Scripts.Render("~/bundles/knockoutmapping")
    @RenderSection("scripts", required: false)
</body>
</html>

という感じで、Knockout.js を画面側で読みこめるように設定しておく必要がある。

簡単な使い方

以下にサンプルコードを記載してみる。

  • Person.cs Name, Age プロパティはさておき、Summary プロパティに Computed 属性を付与している点に注目していただきたい。Computed 属性を付与することで、JavaScript 側にロジックを自動生成することができる。
using System.ComponentModel.DataAnnotations;
using DelegateDecompiler;

namespace KnockoutMVCSample.Models
{
    public class Person
    {
        [Display(Name = "名前"), Required]
        public string Name { get; set; }
        [Required, Display(Name = "年齢"), Range(typeof(int), "0", "200")]
        public int Age { get; set; }

        [Computed]
        public string Summary
        {
            get
            {
                var ret = Name + "は";
                if (Age < 20)
                {
                    ret += "まだまだ未成熟な果実(/ω\)イヤン";
                }
                else
                {
                    ret += "熟れた果実(*´Д`)ハァハァ";
                }
                return ret;
            }
        }

        // 後述するが、 ToString を上書きすると死ぬ
        //public override string ToString()
        //{
        //    return string.Format("私は {0}、{1}歳☆(ゝω・)vキャピ", this.Name, this.Age);
        //}
    }
}
  • HomeController.cs 通常の ASP.NET MVC のコントローラであり、特に留意する点はない
using System.Web.Mvc;
using KnockoutMVCSample.Models;
using System.Diagnostics;

namespace KnockoutMVCSample.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new Person()
            {
                Name = "幼女",
                Age = 9
            });
        }

        [HttpPost]
        public ActionResult Index(Person person)
        {
            Trace.WriteLine(string.Format("私は {0}、{1}歳☆(ゝω・)vキャピ", person.Name, person.Age));
            return View(person);
        }

        //中略
    }
}
  • Home/Index.cshtml 画面最初に Html.CreateKnockoutContext() を利用して Knockout MVC 用のインスタンスを作成しており、フォーム部品を作成する際に同インスタンスを利用している点に注目してほしい
@using PerpetuumSoft.Knockout
@model KnockoutMVCSample.Models.Person

@{
    ViewBag.Title = "Home Page";
    var ko = Html.CreateKnockoutContext();
}

@using (Html.BeginForm())
{
    <br /><br />
    <div>
        <h3>knockout.js でのマッピング</h3>
        @Html.LabelFor(m => m.Name) : @ko.Html.TextBox(m => m.Name, new { value = Model.Name, name = "Name" }) @Html.ValidationMessageFor(m => m.Name)<br />
        @Html.LabelFor(m => m.Age) : @ko.Html.TextBox(m => m.Age, new { value = Model.Age, name = "Age" }) @Html.ValidationMessageFor(m => m.Age)<input id="update_button" type="button" value="年齢+1" /> <br />
        <div>
            名前と年齢についてのサマリ:@ko.Html.Span(m => m.Summary, new { id = "summary" })
        </div>
        <br />        
        <input type="submit" name="submit" value="送付" /><br />
    </div>
}
@section scripts
{
    @ko.Apply(Model)
    <script type="text/javascript">
        $(function() {
            $('#update_button').click(function() {
                $('#Age').text(viewModel.Age(viewModel.Age() + 1));
            });
        });
    </script>
}

更に、「注意点」のスクリーンショットを参照して頂ければわかると思うが、Knockout MVC は viewModel という変数名でモデルを生成するため、上記の記載方法で画面上で JavaScript の viewModel を操作することができる。

注意点

Computed 属性を付与しない場合、以下の様に JavaScript 側の計算用ロジックが自動生成されない。
f:id:waritohutsu:20140113134149j:plain

また、モデルクラス(今回は Person.cs) の ToString() メソッドをオーバーライドすると、以下の様に JavaScript 側の自動生成コードがおかしくなってしまう点にも注意が必要だ。
f:id:waritohutsu:20140113134151j:plain

参考

Knockout MVC は以下の様にサンプルが豊富なため、こちらも参照頂きたい。

Windows Azure SDK for .NET の Management Library を利用して Webサイト の構成を変更する

前回の記事である Windows Azure SDK for .NET の Management Library を利用して Webサイト を動的に作成してみる で疎通までの簡単な手順とWebサイトの作成までを紹介したが、今回は Web サイトの設定情報を更新する方法を紹介する。

サンプルコードを実行する

ダラダラと文章を書いても仕方ないので、以下に試したコードと実行結果を記載する。作成済みの Web サイトの情報を取得する際に、GetAync() メソッドと GetConfigurationAsync() メソッドで取得できる情報が異なる点に注意が必要だ*1

  • サンプルコード
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            ChangeWebSiteStatus();
            Console.ReadLine();
        }

        public static async void ChangeWebSiteStatus()
        {
            const string webSiteName = "createfromcode";
            const string webSpaceName = "eastasiawebspace";

            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<パスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (var client = CloudContext.Clients.CreateWebSiteManagementClient(credentials))
            {
                // まず Web サイトの状態を取得
                {
                    var parameters = new WebSiteGetParameters();
                    //以下を試したけど取れなかった。これなんだろ?
                    //{
                    //    PropertiesToInclude = new string[] { "WebSocketsEnabled", "PhpVersion" }
                    //};
                    var response = await client.WebSites.GetAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のサイトモード    = {1} ", webSiteName, response.WebSite.SiteMode);
                    //以下二つは取れない(key がないので例外が出る)
                    //Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSite.SiteProperties.AppSettings["WebSocketsEnabled"]);
                    //Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.WebSite.SiteProperties.AppSettings["PhpVersion"]);
                }

                // Web サイトの構成情報を取るのはこっち
                {
                    var response = await client.WebSites.GetConfigurationAsync(webSpaceName, webSiteName, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSocketsEnabled);
                    Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.PhpVersion);
                }

                // 構成を更新
                {
                    // PHP バージョンを 5.4-> 5.5, WebSocket を Enabled -> Disabled へ
                    var parameters = new WebSiteUpdateConfigurationParameters
                    {
                        PhpVersion = "5.5",
                        WebSocketsEnabled = false
                    };
                    var updateConfigurationResponse = await client.WebSites.UpdateConfigurationAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    //こっちには WebSite プロパティがない
                    Console.WriteLine("RequestId = {0}, StatusCode= {1} ", updateConfigurationResponse.RequestId, updateConfigurationResponse.StatusCode);
                }

                // Web サイトの状態を 無料 -> 共有 へ変更
                {
                    var parameters = new WebSiteUpdateParameters
                    {
                        SiteMode = WebSiteMode.Basic,
                    };
                    var response = await client.WebSites.UpdateAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    Console.WriteLine("変更後, {0} のサイトモード    = {1} ", webSiteName, response.WebSite.SiteMode);
                }
                // もっかい Web サイトの構成情報を取る
                {
                    var response = await client.WebSites.GetConfigurationAsync(webSpaceName, webSiteName, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSocketsEnabled);
                    Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.PhpVersion);
                }
            }
        }
    }
}
  • 実行結果
変更前, createfromcode のサイトモード    = Limited
変更前, createfromcode のWebScoketモード = True
変更前, createfromcode のPHPバージョン   = 5.4
RequestId = a4f9803ff1a75c2db950db5d938b1ec6, StatusCode= OK
変更後, createfromcode のサイトモード    = Basic
変更前, createfromcode のWebScoketモード = False
変更前, createfromcode のPHPバージョン   = 5.5

*1:この辺はパケットキャプチャで確認したいところ

Windows Azure SDK for .NET の Management Library を利用して Webサイト を動的に作成してみる

Windows Azure SDK も日々進化しており、管理ポータルで操作している内容を SDK 越しでも実現可能になるようだ。今回は Preview 機能(2014年1月時点)となるが、Windows Azure SDK for .NET の Management Library 機能を利用して C# で動的に Webサイトを構築するまでの流れを紹介する。

準備と疎通

Windows Azure SDK for .NET の Preview 機能を利用するためには以下を実行する必要がある。

  • Windows SDK for Windows 8.1 をインストールする(makecert, certmgr コマンドを利用するため)
  • 自己証明書(*.cer)を作成し、管理ポータルにアップロードする
  • 新規にC#プロジェクト作成し、自己証明書から作成する秘密鍵(*.pfx)を配置する
  • NuGet を利用して、Microsoft.WindowsAzure.Management.Libraries のプレビューリリースを取得する。
  • 疎通用のコードを書く
自己証明書(*.cer)を作成し、管理ポータルにアップロードする

事前に Windows SDK for Windows 8.1 をインストールしてもらっているとして、さっそく証明書の作成にはいる。

まず、cmd.exe 直でなく VS2012 ARM Cross Tools Command Prompt 等の makecert.exe や certmgr.exe 実行ファイルが格納されたフォルダ(例:C:\Program Files (x86)\Windows Kits\8.1\bin\x64)にパスを通してくれるコマンドプロンプトを実行する。更に、コマンドプロンプトから以下のコマンドを実行し、ManagementTestCert.cer という名前の自己証明書を作成する。

c:\temp>makecert -r -pe -a sha1 -n "CN=NORMALIAN" -ss my -len 2048 -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" -sy 24 ManagementTestCert.cer

自己証明書を作成した後、Windows Azure の管理ポータルにアクセスし、以下の画面を参考に自己証明書をアップロードする。
f:id:waritohutsu:20140103022939p:plain

新規にC#プロジェクト作成し、自己証明書から作成する秘密鍵(*.pfx)を配置する

自己証明書から秘密鍵を作成する。コマンドプロンプトから certmgr.exe を実行し、作成した自己証明書を選択して[エクスポート]ボタンを押下する(名前は makecert.exe の引数で設定した "CN=NORMALIAN" で表示される)。[証明書エクスポートウィザードの開始]が起動するため、[はい、秘密鍵をエクスポートします]にチェックをし、パスワードを設定して *.pfx ファイルを作成する。この際設定したパスワードは後ほど利用するので注意してほしい。

次に、Visual Studio を起動して新規にアプリケーションを作成する(今回はコンソールアプリケーションとする)。作成したソリューションに *.pfx ファイルを以下のように配置し、プロジェクトの含めた後、[ビルドアクション] を「コンテンツ」に、[出力ディレクトリにコピー]を「新しい場合はコピーする」に設定する。
f:id:waritohutsu:20140103022943p:plain

最後に NuGet のコンソールを起動し、以下のコマンドを実行することで Windows Azure SDK の Preview がインストールできる。

Install-Package Microsoft.WindowsAzure.Management.Libraries -IncludePrerelease
疎通用のコードを書く

以下の疎通用コードを作成することで、実際の疎通が取れる。

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            ListLocationServices();
            Console.ReadLine();
        }

        public static async void ListLocationServices()
        {
            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<秘密鍵のパスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (ManagementClient client = CloudContext.Clients.CreateManagementClient(credentials))
            {
                var result = await client.Locations.ListAsync();
                var locations = result.Locations;
                foreach (var location in locations)
                {
                    Console.WriteLine("Location: {0}", location.Name);

                    foreach (var feature in location.AvailableServices)
                    {
                        Console.WriteLine("\t{0}", feature);
                    }
                }
            }
        }
    }
}

実行結果は以下になる。

Location: East Asia
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: Southeast Asia
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: North Europe
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: West Europe
        Compute
        Storage
        PersistentVMRole
        HighMemory
(中略)

C# のコードで Webサイトを作成してみる

上記の環境まで作成していれば、あとは以下のコードを実行することで East Asia に Web サイトが作成可能だ。

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            CreateNewWebSite();
            Console.ReadLine();
        }

        public static async void CreateNewWebSite()
        {
            const string sitename = "createfromcode";
            const string webSpaceName = "eastasiawebspace";
            const string regionName = "East Asia";

            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<秘密鍵のパスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (var client = CloudContext.Clients.CreateWebSiteManagementClient(credentials))
            {
                // WebSiteCreateParameters が null でもエラー
                // WebSpace が null だとエラー
                // eastasiawebspace 等、WebSpace の名前はある程度決まっている
                var parameters = new WebSiteCreateParameters
                {
                    WebSpace = new WebSiteCreateParameters.WebSpaceDetails
                    {
                        Name = webSpaceName,
                        GeoRegion = regionName,
                        Plan = "VirtualDedicatedPlan",
                    },
                    // カスタムドメイン名を指定できる場所だが、無料モードだと
                    // azurewebsite.net じゃないとバリデーションがかかるっぽい
                    HostNames = new List<string>
                    { 
                        sitename + ".azurewebsites.net",
                    },
                    Name = sitename,
                    WebSpaceName = webSpaceName,
                };
                await client.WebSites.CreateAsync(webSpaceName, parameters, CancellationToken.None);
            }
        }
    }
}