PowerApps と Azure を連携させるベストプラクティス

`ローコード開発はチームスポーツだ！` なんて言葉が最近言われます。
ローコード開発だけでは物足りない処理などを、プロ開発者が作成した API を活用することで機能が大きく拡張したアプリケーションが作成できるからです。

Microsoft の場合、その辺が PowerApps と Azure の OneCloud で連携させることができるので非常に便利です。ただその連携方法についてはあまりまだ事例がないので、PowerApps から Azure の API を呼び出す方法、そしてそれを実現するための推奨体制についてまとめておきたいと思います。

# Azure と連携した PowerApps 開発の全体構成について
体制、アーキテクチャ、などを踏まえた全体像は、こんな感じのイメージが理想かと思います。
![スクリーンショット 2022-05-24 15.57.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/c8398e6c-4414-17e6-461a-399e6039943e.png)
`コネクタ管理者チーム` なんて言葉は正式には存在しませんが、プロ開発者が作成した API と、それを使える市民開発者を管理したりする役割を担うチームです。全体構成やナレッジ蓄積などは CCoE がその役割を担います。

今回は Azure Functions の作成と、それを API Management　にホストさせてSwaggerを Export する方法、そして API Management にホストさせた API を PowerApps から呼び出すための設定についてまとめていきたいと思います。

# Azure Functions の作成
まずは Azure Functions を作成します。今回は Azure Portal からシンプルな HTTP Trigger の Functions を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/cc917d15-a3b6-1a96-9d4b-1dc0581f5978.png)
このままでは返す値が JSON ではないため、message を含んだ JSON を返すよう少し設定を変更します。最終的に作成した関数はこちらです。

```run.csx
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    string name = req.Query["name"];

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic data = JsonConvert.DeserializeObject(requestBody);
    name = name ?? data?.name;

    string responseMessage = string.IsNullOrEmpty(name)
        ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                : $"Hello, {name}. This HTTP triggered function executed successfully.";

            return new OkObjectResult(new FunctionResult { Message = responseMessage });
}

public class FunctionResult
{
        public string Message { get; set; }
    
}
```
関数が作成できたら、コードとテストで HTTP 応答コンテンツを確認します。 クエリに入れた name を含んだ message が返ってきていれば成功です。
![スクリーンショット 2022-05-24 18.38.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/ede0289f-d917-b85e-a614-eba97fd35d38.png)
これで Functions は完成です。

# API Management にホスト
次に API Management のリソースを作成します。API Management には従量課金プランがあり、無料で使える分があるので今回のような検証にはこちらを使用してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/db48603f-0645-733a-e77e-de9e085ed00b.png)
API Management の API のメニューから作成した Functions を選択します。この際 GUI ベースで　Functions を選択できる形になっているので非常に便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/75dda427-c45e-5025-abb6-2b3ba34d85eb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/5dac594f-fd58-1d95-6d03-f8dbd2a8b863.png)
無事追加ができたら、Functions　でテストした時と同様に、API Management でもテストを実行してみます。問題なく動くことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/5310df16-df11-3791-3502-b261c0dc8273.png)
次に API を Export してみます。swagger ファイルが自動的に生成されダウンロードできます。今回は PowerApps に読み込ませるために OpenAPI v2 (JSON) を選択しダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/4114aa97-0341-2627-36c3-c4a8dc083332.png)
次に API Management にアクセスするためのキー情報をメモ帳などにコピペしておきます。これはあとで PowerApps のコネクタ作成時に使用するので大事に保存しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/d1b98649-c68b-488a-afbb-c798020b6415.png)
これで API Management 側の設定は完了です。API Management では Policy を設定することができたり、AAD のアクセス制御ができたりします。今回の記事ではそこまで触れませんが、API Management を介することでコネクタ管理チームがしっかりガバナンスを効かせながらコネクタを運用することが可能です。

(参考) API Management でポリシーを適用する
https://docs.microsoft.com/ja-jp/azure/api-management/api-management-policies

# Swagger 作成
次に API Management から Export した Swagger ファイルを編集していきます。実はコネクタを作る上ではここが肝だったりします。しっかり Swagger ファイルを定義することで、PowerApps でアプリを作成している時に予測変換が出てきます。市民開発者がアプリを開発しやすくするためにはこの Swagger ファイルの生成が非常に重要です。

今回の場合、シンプルに名前を返すだけですが、message のプロパティをしっかり定義しておきます。今回作成した　Swagger ファイルはこんな感じです。

```komiyasafunctions.swagger.json
{
    "swagger": "2.0",
    "info": {
        "title": "komiyasafunctions",
        "version": "1.0",
        "description": "Import from \"komiyasafunctions\" Function App"
    },
    "host": "komiyasaapim1.azure-api.net",
    "basePath": "/komiyasafunctions",
    "schemes": [
        "https"
    ],
    "securityDefinitions": {
        "apiKeyHeader": {
            "type": "apiKey",
            "name": "Ocp-Apim-Subscription-Key",
            "in": "header"
        },
        "apiKeyQuery": {
            "type": "apiKey",
            "name": "subscription-key",
            "in": "query"
        }
    },
    "security": [
        {
            "apiKeyHeader": []
        },
        {
            "apiKeyQuery": []
        }
    ],
    "paths": {
        "/HttpTrigger1": {
            "get": {
                "operationId": "get-httptrigger1",
                "parameters": [
                    {
                        "name": "name",
                        "in": "query",
                        "description": "name",
                        "required": true,
                        "type": "string"
                    }
                ],
                "summary": "HttpTrigger1",
                "responses": {
                    "200": {
                        "description": "success!!",
                        "schema": {
                            "$ref" : "#/definitions/function1Result"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "function1Result" : {
            "type" : "object",
            "properties" : {
                "message" : {
                    "type" : "string"
                }
            }
        }
    },
    "tags": []
}
```
この Swagger ファイルをしっかり作成しておかないと、PowerApps で呼び出す関数の変数の値が違いますとかでてきたりしますので、しっかりと記載することが重要です。(大事にことなので2回書きました笑)
Swagger　ファイルをコネクタ管理者チームでしっかり管理することで市民開発者がアプリを開発しやすくなります。この Swagger ファイルの管理も GitHub や Azure DevOps などを用いてコード管理するのも良いかもしれません。

# コネクタの作成とテスト
作成した Swagger ファイル をカスタムコネクタとして読み込むだけでコネクタが作成できます。カスタムコネクタの追加は左側のメニューのデータは以下にある `カスタムコネクタ` から作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/6c2f6a26-4165-1020-c893-c866396bc110.png)
カスタムコネクタの作成方法はいくつかありますが、今回は `OpenAPI ファイルをインポートします` を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/e86ea2ca-7f86-b93e-e7fb-4f54360ed901.png)
コネクタ名を入力し、作成した　Swagger ファイルをインポートするとコネクタが作成されます。ここのコネクタ名は PowerApps 上で表示されるコネクタ名で、実際に PowerApps の関数などの設定値の際には Swagger ファイルの中身で定義されてい名前が出てきます。少しややこしいですが、わかりやすい名前で設定しておくことをおすすめします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/8ec1f092-9b2c-9678-f3d5-d22671ad1267.png)
追加したあと諸々設定値が出てきますが、Swagger ファイルで定義されているものがそのまま出てきているだけですので、全て規定値のまま進みます。カスタムコネクタが追加されるとテストができるようになりますので、接続を試してみます。この際に API Management の Key 情報を入力することが必要となりますので、コピペしておいた Key 情報を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/74188680-214c-5998-d75f-105a1fde369b.png)
Key 情報も入力されコネクタが追加されるとテストを実行できるようになります。name パラメータに適当な値を設定してテスト操作を行なってみます。ボディに帰ってくる JSON が表示されるので、以下のようなメッセージが出てきていれば問題なく追加されていることが確認できます。
![スクリーンショット 2022-05-24 19.10.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/5b8948c6-fe2e-15ef-b86d-fce84733dc97.png)
```JSON
{
  "message": "Hello, Kosuke. This HTTP triggered function executed successfully."
}
```
これでコネクタの追加は成功です！ここまでがコネクタ管理者チームの役割かなーと思います。

# PowerApps のアプリからコネクタを呼び出す方法
最後に PowerApps カスタムコネクタを使用してアプリを開発してみます。テキストを入力して Submit したらそれがクエリとなって Functions が呼び出され、帰ってきた値が PowerApps 上に表示される、というアプリを作ってみたいと思います。
まずはじめにコネクタを追加します。左メニューのデータ追加から追加したコネクタ名で検索をかけると先ほど追加したコネクタの名前がでてきます。※カスタムコネクタを呼び出すにはプレミアムコネクタが必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/fb35b4a0-087d-542a-8d59-5d8ff4e1a1f9.png)
次にこんな感じで必要なコンポーネントを追加します。今回必要なコンポーネントは、 `テキスト入力`、`ボタン`、`テキストラベル`の三つです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/39927a92-2479-7898-2845-279bc8dd46df.png)
必要なコンポーネントを追加したら、ボタンの `OnSelect` 処理に以下のような関数を書きます。

```
Set(result, komiyasafunctions.gethttptrigger1(TextInput1.Text))
```
追加したラベルの中身は `result.message` としておきます。さぁ実行してみましょう！
![スクリーンショット 2022-05-24 19.31.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/c01da666-fdf5-8351-20c3-cde835016e2f.png)
無事に実行し結果が表示されました！ 
エラーが発生する場合は監視を確認して、どんなエラーが発生しているか確認すると良いかと思います。このエラーなどの問い合わせは CCoE チームもしくはプロ開発者チームに投げるようなフローにしておけばスムーズに市民開発を行えるかと思います。
![スクリーンショット 2022-05-24 19.35.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/5358349b-5242-b05f-9416-63464aad51d3.png)

# まとめ
`ローコードはチームスポーツ！` これが Azure と連携させた PowerApps アプリを作るためのキーワードかと思います。エンジニア人口が減少傾向にある日本において、うまくリソースを活用し、作れるところは市民開発者がローコードで作る、という取り組みは重要かと思います。プロ開発者、市民開発者、そしてコネクタ管理者が手を取り合って素敵なアプリケーションをサクサク作成できる、そんな風になっていったら良いなーと記事を書きながら思いました。

# 参考リンク
今回の記事は以下のブログが大変参考になりました。Functions で変更するべきことや Swagger 定義も動画でわかりやすくまとまっています。
(参考)PowerApps から Azure Functions を使うメモ
https://zenn.dev/okazuki/articles/azure-function-powerapps