Azure AppService もしくは Functions で Site Extensions を作るメモ

Azure Functions V4 が リリースされて .NET 6 が使えるようになった。それと同時に Proxy が廃止されたので、しばやんさんが、いい感じの代替えソリューションをリリースしてくれました。

https://github.com/shibayan/AppServiceProxy.SiteExtension

この仕組みは、Site Extensions という仕組みの上で作成されているのですが、私は、Site Extensions の仕組みを知らないので調査をしてみることにしました。

# 概要

Site Extensions は、AppSerivce や、Functions で使える仕組み。[Kudu](https://docs.microsoft.com/en-us/azure/app-service/resources-kudu#:~:text=Kudu%20is%20the%20engine%20behind%20a%20number%20of,by%20HTTPS.%20This%20Kudu%20app%20is%20accessible%20at%3A) を使った仕組みで、開発者がIISのバックエンドになりうるアプリケーションを書いて kudu に配置することができ、設定ファイルによって、IIS の `applicationHost.config` を書き換えることが出来る仕組みです。kudu の中で動作するので、セキュリティで守られた環境にあるため、Adminツールを作るのにも向いています。

# チュートリアル
この仕組みが作られたのが古いためか、最新のドキュメントは見つかりませんでした。2014 年ベースのものがここで見つかりました。

[Writing a Site Extension for Azure Websites](https://azure.microsoft.com/en-us/blog/writing-a-site-extension-for-azure-websites/)

今回は折角ですので、.NET 6 ベースのアプリケーションで同じことをやってみました。チュートリアルでは、ASP.NET MVC を使っていますので、同じように .NET 6 ベースのものを使ってみます。チュートリアルでは `FileCounter` というプロジェクトを作りますので、同じ名前にしてみます。デフォルトで、テンプレートが作成されます。

元のチュートリアルでは、`Server.MapPath("/")` というメソッドを使うのですが、今のバージョンには存在しません。代替えソリューションとしては、DI で、`IWebHostEnvironment` をインジェクションして、そのオブジェクトが所有している `WebRootPath` を使うようにします。元のサンプルでは、このインターフェイスを使っていなかったので、Azure でもローカルでも実行できるように、if ステートメントで分岐していましたが、不要なので削除しました。

私のサンプルでは、コードを変えてファイルをリストするように変えています。

```CSharp:HomeController
   public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private IWebHostEnvironment _environment;

        public HomeController(IWebHostEnvironment environment, ILogger<HomeController> logger)
        {
            this._environment = environment;
            _logger = logger;
        }

        public IActionResult Index()
        {
            string siteFolder = _environment.WebRootPath;

            var files = Directory.GetFiles(siteFolder,
                "*.*",
                SearchOption.AllDirectories);
            var fileNames = string.Join(", ", files);

            return View(model: fileNames);
        }
```

`Index.cshtml` は、ファイルのリストを model として渡しているので、次のように変えました。

```Csharp:Index.cshtml
@model string
@{
    ViewData["Title"] = "File Listing MVC";
}

<div class="text-center">
    <h1 class="display-4">Your site following files</h1>
    @Model
</div>

```

`F5`を押して実行してローカルで動作したら、プロジェクトに、`applicationHost.xdlt` をプロジェクトのルートに置いておきます。VS の場合、`Copy to Output Directory` の設定を`Copy if newer`などにしておくのが重要です。

## ApplicationHost.xdlt

これが、IIS の [ApplicationHost.config](https://docs.microsoft.com/en-us/iis/get-started/planning-your-iis-architecture/introduction-to-applicationhostconfig#:~:text=ApplicationHost.config%20is%20the%20root%20file%20of%20the%20configuration,and%20the%20root%20web.config%20for%20.NET%20Framework%20settings%29.)を変更するための設定ファイルです。この設定の場合、scm サイトの方に`/filecounterMVC` というサブディレクトリにアクセスしたときのこのアプリケーションが動作して結果を返します。

```xml:applicationHost.xdlt
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <system.applicationHost>
    <sites>
      <site name="%XDT_SCMSITENAME%" xdt:Locator="Match(name)">
        <application path="/filecounterMVC" applicationPool="%XDT_APPPOOLNAME%" xdt:Transform="Insert">
          <virtualDirectory path="/" physicalPath="%XDT_EXTENSIONPATH%" />
        </application>  
      </site>
    </sites>
  </system.applicationHost>
</configuration>
```

これを kudu のサイトでテストするためには、プロジェクトを右クリックして、Publish を選択して、Folder を選択して、パッケージを作ります。Publish したら、バイナリが作成されているのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/b50eb55f-4b18-bb8b-7c82-a9058482a0d1.png)

Kudu の Deubg console から、SiteExtensions ディレクトリを作成して、その下に自分の作った Extension を置いておきます。ドラッグ＆ドロップで先ほど作成した、アプリケーションをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/f202091d-4758-e9c5-a08d-7f6d759fedca.png)

あとは設定した通りに、サイトにアクセスします。動作しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/30a9c8f2-2388-8092-691f-a9a313cd37d0.png)

ここからは、IIS のコンフィグの知識が必要になるので、まだまだ勉強が必要ですが、例えば私は、`%XDT_SCMSITENAME%` を変更して、本番側のURLにマップしたいと思ったので、`%XDT_SITENAME%` にして、本番側のリスタートを行ったのですが、途中で、kudu が死にました。多分設定が根本的に間違っているのでしょう。このようなケースは、AppSettings に `WEBSITE_PRIVATE_EXTENSIONS=0`を設定すると、Private のExtensionが無効になり、IIS の設定が無効になるのでサイトが回復します。

デバッグの際は、`C:\home\LogFiles\Transform` にトランスフォーメーションのログがあります。また、変更された`applicationhost.config`は、地球アイコン (Site の意味)をクリックしてその配下の`Config` に実態が存在します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/07520532-bd78-4ef8-984c-dd230e6ddcd9.png)

`%XDT_SITENAME%`に変更したものを見てみるとこんな感じになっていました。多分問題なのでしょう。（これから勉強しますｗ）

```xml:ApplicationHost.config
<application path="/filecounterMVC" applicationPool="tsushiextension">
  <virtualDirectory path="/" physicalPath="C:\home\SiteExtensions\FileCounterMVC" />
</application>
```



# 参考資料
何か SiteExtensions を作る必要があったわけではなく、単なる興味で調べたのでまずはここまでですが、しばやんさんにリソースを教えてもらったので共有します。

### Azure Site Extensions

https://github.com/projectkudu/kudu/wiki/Azure-Site-Extensions?fbclid=IwAR0Em3m3Z-BPmPnB6NuIpc33Myt1DpZMlLhMtKqWVHhC2lbsawFntH-GmIc

これを読めば大体理解できます。他にも正式版として Publish する方法などが載っています。

### Xdt transform samples

https://github.com/projectkudu/kudu/wiki/Xdt-transform-samples?fbclid=IwAR2VqenN2xPjCyOibvi1ZIxMj3wDGe1R1KVTKu3jVIPbT6vGq7Cbc9bVpcQ

設定資料のリファレンスが欲しいところですが、ないみたいなので、サンプルを参照します。しばやんさんの場合、[Azure Functions のもの](https://github.com/Azure/azure-functions-host/blob/dev/src/WebJobs.Script.WebHost/applicationHost.xdt)とかも参照されているようです。Functions もSite Extensions として登録されるようですね。

### IIS 関連の資料
今後自分の読書メモとして、IIS の `ApplicationHost.config`の設定の書き方を理解する必要がありそうです。

https://docs.microsoft.com/en-us/iis/get-started/planning-your-iis-architecture/introduction-to-applicationhostconfig#:~:text=ApplicationHost.config%20is%20the%20root%20file%20of%20the%20configuration,and%20the%20root%20web.config%20for%20.NET%20Framework%20settings%29

https://docs.microsoft.com/en-us/iis/get-started/planning-your-iis-architecture/understanding-sites-applications-and-virtual-directories-on-iis