ぷろじぇくと、みすじら。

App Service PlanとApp Serviceをリソースグループ間で移動する場合の制限

Created at: | Tag: Azure

AzureのApp Service PlanとApp Serviceをリソースグループ間で移動する場合には制限事項があり、その手順に沿って移動しないとうまくいきません。…というのにはまりました。

ドキュメントのApp Serviceの制限事項には以下のように書かれています。

App Service アプリを使用している場合、App Service プランのみを移動することはできません。 App Service アプリを移動するには、次のオプションがあります。

  • App Service プランとそのリソース グループ内の他のすべての App Service リソースを、まだ App Service リソースが含まれていない新しいリソース グループに移動する。 この要件により、App Service プランに関連付けられていない App Service リソースも移動する必要があります。
  • アプリを別のリソース グループに移動し、元のリソース グループにも App Service プランをすべて保持する。

アプリが正常に動作するために、App Service プランがそのアプリと同じリソース グループ内に存在する必要はありません。

これ(とそれに続く説明)はどういうことなのかというのがピンとこないので順を追って移動する例を見ていきます。

初期構成の想定

まずは初期構成として以下のような構成になっているとします。

最終的な構成

次に移動した結果として希望したい構成です。

Step 1: 新しいグループを作ってAppServiceとともに移動する

まずは新しいリソースグループ BasicPlan を作って、App Service Planを移動します。この際、App Service Planの移動とそれに付随するリソース(App Serviceなど)はすべて新しいリソースグループに移動する必要があります。

Step 2: 他のリソースも新しいグループに一度まとめる

次にもう一つのApp Serviceも一度新しいリソースグループ BasicPlan に移動します。 App Service自体の移動にはApp Service Planは必要ないのでそのまま移動できます。

Step 3: App Serviceを移動する

次に新しいリソースグループ App1Group, App2Group を作り、そこにApp Serviceのみを移動します。

Step 4: App Service以外を移動する

最後にApp Service以外を移動します。

注意事項

az resource move (azure-cli)で移動する

Azure ポータルからではなくazure-cliを利用して移動すると、バリデーションがかからずバラバラに移動できてしまいポータルよりいいと思いきや、想定外の形になることがあるようなので素直にポータルからやっていきましょう。

原則として1つのResource Groupに一つ以上WebApp リソース(App Service Plan)を移動できない

App Service Planの移動先にすでにMicrosoft.Web以下のリソースタイプなどがあると移動できません。ドキュメントのまだ App Service リソースが含まれていない新しいリソース グループに移動するというのはこの制限事項のことを指しています。

この状態で移動しようとすると以下のようなエラーメッセージが表示されます。

There was an error moving resources. Resource move validation failed. Please see details. Diagnostic information: timestamp '20170805T164541Z', subscription id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', tracking id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', request correlation id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'. (Code: ResourceMoveProviderValidationFailed) Cannot move WebApp resources to a resource group that already contains WebApp resources. Please ensure destination resource group Common does not have any WebApp resources in it before performing the next move operation. Or create a new resource group and move resources there. (Code: BadRequest, Target: Microsoft.Web/serverFarms)

App Service Planの移動と同時にすでにバラバラになっているApp Serviceを一度移動する必要がある

App Service Planを移動するときには関連したApp Serviceも同時に移動する必要があります。つまり一度App Service Planを移動する前にApp Serviceを同じリソースグループに移動する必要があります。

ドキュメントではWeb アプリがその App Service プランとは異なるリソース グループに存在するが、その両方を新しいリソース グループに移動する場合、移動を 2 段階で行う必要がありますとして解説されているのがこれです。

この状態で移動しようとすると以下のようなエラーメッセージが表示されます。

There was an error moving resources. Resource move validation failed. Please see details. Diagnostic information: timestamp '20170805T175159Z', subscription id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', tracking id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', request correlation id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'. (Code: ResourceMoveProviderValidationFailed) Cannot move resources because some site(s) are located in other resource group(s) but hosted by resource group 'Default-Web-JapanEast'. The list of site(s) and corresponding resource groups: 'website-01:Default-Web-JapanEast,website-02:Default-Web-JapanEast'. This may be a result of prior move operations. Move the site(s) back to respective hosting resource group(s) and try again. (Code: BadRequest, Target: Microsoft.Web/serverFarms)

Microsoft.Web/certificates リソースタイプは移動不能

証明書はApp Service Planに関連付けられ、そのリソースグループに移動不能な形で残るので最悪削除する羽目になります(新しいリソースグループで証明書を追加できなくなる場合がある)。

まとめ

移動するのは手順はともかくとしても処理に時間がかかるので、App Service Planとリソースグループの構成は最初からちゃんと考えておいたほうがいいでしょう。まあ真面目に使うときには考えて作るとは思いますが…。

ASP.NET MVC Core 1.1のRazorテンプレートでC#7を使う

Created at: | Tag: CSharp ASP.NET

ASP.NET MVC Core 1.1でプロジェクトを作るとRazorテンプレート(cshtml)では利用できるC#のバージョンは6です。2017年7月時点の最新版であるところのC#7の機能を使うためにはちょっと準備が必要です。

NuGetパッケージをインストール

まず以下の二つのNuGetパッケージをインストールします。

System.ValueTupleは入れなくてもいいといえばいいですがもちろんその場合にはValueTupleを使えません。

Razorの設定

パッケージのインストールができたら次はASP.NET MVC Coreの設定をするためにStartup.csを開きます。

ConfigureServicesメソッドに以下の行のが見つかるかと思います。

services.AddMvc();

その行を以下のように書き換えます。

services.AddMvc()
    .AddRazorOptions(options =>
    {
        options.ParseOptions = new CSharpParseOptions(LanguageVersion.Latest);
    });

AddRazorOptionsでRazorの設定をする形です。LanguageVersion列挙体にはCSharp7をはじめとしてバージョンがいろいろあるのですが大抵は最新を指すLatestでもいいような気がします。

これで完了です。あとはC# 7の構文がエラーにならなければできあがりです。

OneDrive File On-Demandについて

Created at: | Tag: OneDrive Windows

OneDrive File On-Demandについて先日行われたWindows Insider Meetup in Tokyo #2に飛び込みで少しお話してきました。

OneDrive On-Demandがすごい

Windows 8.1の時にもプレースホルダという機能は存在していて、その後無くなって、Windows 10 Fall Creators Updateと呼ばれる次期アップデートで復活するということになったのですがわざわざ時間をかけて復活してきたということは結構違うのでは?と思って少し調べたのがきっかけです。

記憶が定かではなく、かつ検証もしてないのですが確かWindows 8.1のプレースホルダはプログラムから直接扱おうとすると0バイトの謎ファイルになっていたような記憶があります。あくまでシェルが良しなにしてくれるショートカットに近い扱いというか。

ところが今回復活するプレースホルダはファイルシステムレベルで考慮されている感があって、互換性やシームレスさが高まることが期待されます。

余談

Windowsの開発用にGVFS(Git Virtual File System)というGitのためのファイルシステムを作ったと少し前に発表していましたが、GVFSはCreators Update以降で動作するというあたり、もしかして何かファイルシステム周りにAPIが増えたのかもという気がしています。

今までもDokanなどいわゆるFUSEのようなものがありましたが、もう少し作りやすい仕組みを整備しているのかもですね。UIもクラウドプロバイダからダウンロード中みたいな表示だったりするのでOneDrive以外のDropboxなどでも対応できるようにしてくるのかもしれません。

.NETのバージョンまとめメモ

Created at: | Tag: .NET ASP.NET

.NET Coreとそれにまつわるバージョンはいろいろあって、多分数字が大きい方が正しいのであろうと思いつつも1.1と1.0が並行していた時期があったりとどっちを使えば…と若干混乱したことがあったので整理してみようかと。.NET Standardというものもあって頭が爆発しそうですし。

なお.NET Standardに関して詳しくは.NET Standardのページと、藤原さんの.NET Standard のおさらいがおすすめです。

バージョン一覧

2017年6月9日現在の最新の.NET Core/.NET/ASP.NET Core/ASP.NET MVC Coreのバージョンとそれに対応する.NET Standardバージョンの一覧です。

Product Release Name Latest .NET Standard
.NET Core
(netcoreapp)
LTS .NET Core 1.0 1.0.5 (SDK 1.0.4) 1.6
Current .NET Core 1.1 1.1.2
Preview .NET Core 2.0 2.0-preview (VS2017.3) 2.0
ASP.NET Core LTS ASP.NET Core 1.0 1.0.4 n/a
Current ASP.NET Core 1.1
(.NET Core 1.1, .NET 4.6)
1.1.2
Preview ASP.NET Core 2.0
(.NET Core 2.0, .NET Standard 2.0予定)
2.0-preview
ASP.NET MVC Core LTS ASP.NET Core 1.0 1.0.3 n/a
Current ASP.NET MVC Core 1.1 1.1.3
Preview ASP.NET MVC Core 2.0 2.0.0-preview
.NET Framework - .NET Framework 4.5 4.5.2 1.2
- .NET Framework 4.6 4.6.2 1.5(w/Tooling 1.0 or 2.0), 1.6(w/Tooling 2.0), 2.0
Current .NET Framework 4.7 4.7 1.5, 1.6, 2.0

ASP.NET Core/ASP.NET MVC Coreはランタイムではないので.NET Standardには関係ありません。

インストールするときに微妙に混乱することのあった点として.NET Core SDKバージョンやToolingといったものがあるということでしょうか。特にSDKバージョンは.NET Coreのバージョンと一致していないのでわかりにくいです。

.NETとASP.NET Coreのバージョン選択

.NET Coreは最新版を素直に使えば良いので1.1シリーズでよいでしょう。

.NET Frameworkは4.7が最新なのですがSystem.ValueTupleがコアライブラリ入りしたおかげで、.NET Standardなライブラリ側でSystem.ValueTupleパッケージのNuGet参照があると参照が混乱して無事死亡というひどい問題があるので4.7を選ぶときには気を付けましょう。

ASP.NET Core/ASP.NET MVC Coreは最新で良さそうです。

.NET Standardの選択

.NET Coreをターゲットにする分に関してはまあ1.0-1.6どれでもという感じです。

.NET Frameworkがややこしく.NET Framework 4.6で.NET Standardは1.6にするにはTooling 2.0である必要がある、ということです。そもそもTooling 1.0/2.0とはという話になって訳が分からないので1.5以下を選ぶのが無難そうです。

なお.NET Standard 1.5は.NET Framework 4.6.2以降なので気を付けないと.NET Framework 4.6がターゲットにならないものになります。1.4でも4.6.1ですけど…。ややこしい。

そういった点を鑑みると以下のような感じで選ぶのがよさそうかなと思いました。

もちろん使いたいものによって選択するものは変わってくると思います。古いランタイムをターゲットにしている場合にはPCLやShared Projectで#ifの方が良かったりするかもしれません(Unityとか)。

まとめ

.NET Standard 2.0はよ。

LINQPadのコードにファイルをドラッグアンドドロップで渡したい

Created at: | Tag: CSharp LINQPad

LINQPadでちょっとしたツールやバッチスクリプト的なものを作ったときに処理対象のとしてファイルを取ることはよくあるのですが、大抵はコードにパスを直接書いていることがほとんどではないでしょうか。

ただ便利なものは往々にして対象ファイルを変えたりしたいとか誰かに渡したいこともあり、そんな時処理対象のファイルをドラッグアンドドロップでコードに渡せれば…と思ったりすることもあります。残念ながらLINQPadに標準で備わっているユーザー入力と言えば Console.ReadLineHyperlinq ぐらいでドロップを受け付けるユーティリティは備わっていません。

その時点で普通のWindowsアプリケーションとして作りましょうという気もするのですがそうはいってもめんどくさいですよね。というわけでなんとか渡せるようにする方法です。

解決策

LINQPadは通常出力をHTMLベースで行っていますが、自力でHTML以外のカスタム出力結果パネルを出すこともできます。カスタムな出力結果パネルはWindows FormsやWPFのコントロールをホストできるというものなので、それを利用して通常のWindowsのアプリケーションと同様にドラッグアンドドロップを受け付けるという形でドロップターゲット機能を実現できます。

というわけで以下のようなメソッドを用意することでドロップ用のパネルを表示し、ドロップしたファイルのパスを返せます。

// PresentationFramework.dll と PresentationCore.dll を References に追加しておきます
Task<string[]> WaitForFileDrop()
{
    var tcs = new TaskCompletionSource<string[]>();
    var running = Util.KeepRunning();
    var button = new System.Windows.Controls.Button
    {
        Content = "Drop Here",
        AllowDrop = true,
    };
    var outputPanel = PanelManager.DisplayWpfElement(button, "File Drop");
    button.DragEnter += (sender, e) =>
    {
        e.Effects = System.Windows.DragDropEffects.Copy;
    };
    button.Drop += (sender, e) =>
    {
        var dropPaths = e.Data.GetData(System.Windows.DataFormats.FileDrop) as string[];

        running.Dispose();
        outputPanel.Close();

        tcs.SetResult(dropPaths);
    };

    return tcs.Task;
}

やっていることは単純でドロップを受け付けるボタンを作り、出力パネルとして表示し、ドロップされたらパスを取り出して TaskCompilationSource<T> を通して呼びもとに返すだけです。

使い方はこんな感じです。必要に応じてMyExtensionsなどに収めておくと使い勝手がいいでしょう。

async Task Main()
{
    // ドロップを待ち受け
    var paths = await WaitForFileDrop();

    // ドロップされたファイルのパスを出力
    paths.Dump();

    // ドロップされたファイルを画像として表示
    paths.Select(x => Util.Image(x)).Dump();
}

これで人に渡したりバッチ的に使うときにちょっと便利に使えるようになるかもしれません。どうぞご利用ください。