【Microsoft Flow】ワークフロー定義を利用したフローの管理・公開について考えてみる

# ワークフロー定義とは？

Microsoft Flow では、フローのロジックの内容を定義するための表現として「ワークフロー定義」が用いられています。ワークフロー定義とは、[Azure Logic Apps のワークフロー定義言語のスキーマ参照](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language) によると、

>Azure Logic Apps を使用してロジック アプリのワークフローを作成するときは、ワークフローの基になる定義で、ロジック アプリに対して実行される実際のロジックを記述します。 この記述は、ワークフロー定義言語スキーマによって定義され、検証される構造に従います。このスキーマでは、JavaScript Object Notation (JSON) が使用されます。

とのこと。要するにワークフロー定義とは「ワークフロー定義言語によりフローの内容を記述したJSONスキーマ表現」ということです。（ここでは「Azure Logic Apps」は「Microsoft Flow」と読み換えてください）

Microsoft Flow においては、個別のフロー画面で、以下の[詳細]-[エクスポート]-[Logic Apps テンプレート（.json）]をクリックしてワークフロー定義データをjsonでダウンロードすることができます。もしくは、[パッケージ（.zip）]をクリックしてダウンロードできるzipファイルに含まれる definition.json にも同じ内容が記述されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/91560ec7-d329-ddc7-04fa-4ee75d120a53.png)

上記方法でダウンロードしたjsonデータのうち、以下の部分がワークフロー定義となります。

```json
"definition": {
  "$schema": "<workflow-definition-language-schema-version>",
  "contentVersion": "<workflow-definition-version-number>",
  "parameters": { "<workflow-parameter-definitions>" },
  "triggers": { "<workflow-trigger-definitions>" },
  "actions": { "<workflow-action-definitions>" },
  "outputs": { "<workflow-output-definitions>" }
}
```

# ワークフロー定義でなにができる？

さて、察しの良い方はすでにお気付きかと思いますが、このワークフロー定義を利用して以下のようなことが可能となります。

1. jsonにより記述されているため、Gitでフローの更新の差分管理が行える
2. Githubや技術記事などで公開するだけで、画面キャプチャ付きで作成手順を書かなくても、分かるひとには読むだけで分かる
3. ~~一定の手順を踏むことにより、ほかの環境で同じフローの再現が可能~~（事情は後述）

## 1.jsonにより記述されているため、Gitでフローの更新の差分管理が行える
以下画像例のようにGitで変更履歴の記録・追跡がプログラムのソースコードと同様に行えるようになります。
フロー作成・更新→ワークフロー定義の取得→GitにPushという流れです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/258416/67dd7eb8-f095-8ee2-8b91-864a4fc09dbb.png" width=60%>
*Gitでのワークフロー定義管理例画像*

## 2. Gitや技術記事などで公開するだけで、画面キャプチャ付きで作成手順を書かなくても、「分かるひとには読むだけで分かる」

Microsoft Flow は、GUIから業務自動化・効率化フローを簡単に作成できるというローコーディング性がメリットの機能ですが、作成したフローについて技術記事などでいざ公開したいとなると、記事の筆者は一つ一つのアクションの画面キャプチャを貼り付けながらGUIの設定手順をこと細かに記さなければならず、ノウハウの公開に手間がかかるというデメリットに転じてしまいます。

そこで、ワークフロー定義を利用すれば、記事にjsonをぺぺっと貼り付けて、ポイントとなる処理について解説を入れれば、「分かるひとには読むだけで分かる」記事を即席で作ることができるようになり、イケてるフローが作成できたときのネット公開のハードルがぐっと下がるはずです。

<details><summary>例えばQiitaでならこんな感じに公開して読んでもらうなど</summary><div>

```json-doc
{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "$authentication": {
        "defaultValue": {},
        "type": "SecureObject"
      }
    },
    "triggers": {
      "manual": {
        "type": "Request",
        "kind": "Button",
        "inputs": {
          "schema": {
            "type": "object",
            "required": [],
            "properties": {}
          }
        }
      }
    },
    "actions": {
      "作成": {
        "runAfter": {},
        "type": "Compose",
        "inputs": "testtext"
      }
    },
    "outputs": {}
  }
}
```
</div></details>

## 3.一定の手順を踏むことにより、ほかの環境で同じフローの再現が可能（更新中）

前項は「分かるひとには読むだけで分かる」だけの利用方法なので、せっかく公開されているワークフロー定義からさくっと同じフローを再現できないのか、という願望があるかと思います。

2019年3月現在、[パッケージ(.zip)]の方からダウンロードしたデータであれば、以下のマイ フロー画面の[インポート]からzip形式でのインポートが可能ですが、json形式によるワークフロー定義のインポート方法は用意されていません。
![スクリーンショット 2019-03-16 17.00.33.png](https://qiita-image-store.s3.amazonaws.com/0/258416/0edbb774-d89b-a381-5131-91ae49b46e20.png)

また、[PowerShell support for PowerApps (preview)](https://docs.microsoft.com/ja-jp/power-platform/admin/powerapps-powershell)を見ても、フローを作成するPowerShellコマンドは現在のところ提供されていないようです。

~~一方、Microsoft Flowには[Flow management]というフローを管理するためのアクションが用意されており、そのうちの[Create Flow]アクションを用いればワークフロー定義からフローをインポートすることも可能です。~~

<font color="Red">**[Flow management]の[Create Flow]アクションを用いたインポート手順（後述）の作成中にワークフロー定義からのフロー作成がエラーとなるパターンが想定以上に見つかったため、本項目の続きの作成は現在見合わせ中となります。**</font>

<font color="Red">**例えば、[Flow management]アクションが含まれるフローのワークフロー定義を同手順でインポートすると下記のようなエラーとなります。**</font>
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/6c08f13e-23e6-5188-847d-11a0f8af024e.png)

<font color="Red">**ほかにも、[OneDrive for Business]や[Office 365 Outlook]のアクションが含まれている場合にもインポート後に同様のエラーとなったため、フロー作成時のアクション追加において「Azure AD に対するアクセス許可の委任が必要となるアクション」は[Create Flow]アクションを使用しても正常にインポートができないようです。**

~~そこで、ここからはワークフロー定義をインポートしてフローを作成できるフローの作成手順を以下紹介していきます。~~

<details><summary>現在更新中のため手順は非表示中</summary><div>

## 前提

- Microsoft Flow

## 作成手順

### 1. フローを新規作成

1. Microsoft Flow を起動してマイ フロー画面を表示
2. [新規]-[一から作成]メニューをクリック

### 2. トリガーのボタンを作成
インポートしたいフローのjson定義データをフロー実行時にテキスト入力できるようにします。

1. トリガー一覧より、[手動でフローをトリガーします]を選択
2. [入力の追加]をクリック
3. [テキスト]をクリック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/aaaeaa6a-80f3-f916-c6a9-11ee5322c762.png)

<details><summary>コードビューはこちら</summary><div>

```json-doc
{
    "kind": "Button",
    "inputs": {
        "schema": {
            "type": "object",
            "required": [
                "text"
            ],
            "properties": {
                "text": {
                    "type": "string",
                    "x-ms-dynamically-added": true,
                    "description": "入力を指定してください",
                    "title": "入力",
                    "x-ms-content-hint": "TEXT"
                }
            }
        }
    }
}
```
</div></details>

### 3. JSON の解析アクションの作成

入力したjson定義データからdefinition要素をパースして、次手順のCreate Flowで利用できるようにします。

1. [新しいステップ]をクリック
2. アクション一覧より、[JSON の解析]を選択
3. プロパティを以下のように設定

|プロパティ名|値|
|---|---|
|コンテンツ|*`@triggerBody()['text']`*|
|スキーマ|下記のschema.jsonの内容を設定|

```schema.json
{
    "type": "object",
    "properties": {
        "definition": {
            "type": "object",
            "properties": {}
        }
    }
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/6259ee18-b139-2b4d-822d-792114d9f134.png)




<details><summary>コードビューはこちら</summary><div>

```json-doc
{
    "inputs": {
        "content": "@triggerBody()['text']",
        "schema": {
            "type": "object",
            "properties": {
                "definition": {
                    "type": "object",
                    "properties": {}
                }
            }
        }
    }
}
```
</div></details>

### 4. Create Flowアクションの作成

json定義データをもとにしてフローを作成するアクションを作成します。

1. [新しいステップ]をクリック
2. アクション一覧より、[Create Flow]を選択
3. プロパティを以下のように設定

|プロパティ名|値|
|---|---|
|Environment|任意の環境を選択|
|Flow Display Name|*imported_@{addHours(utcNow(),9)}*|
|Flow Definition|*@{body('JSON_の解析')?['definition']}*|
|Flow State|*Stopped*|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/f160add2-ffd3-4683-3966-001411470f18.png)


<details><summary>コードビューはこちら</summary><div>


環境名を'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'でマスクしています。

```json-doc
{
    "inputs": {
        "host": {
            "connection": {
                "name": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$connections']['shared_flowmanagement']['connectionId']"
            }
        },
        "method": "post",
        "body": {
            "properties": {
                "displayName": "imported_@{addHours(utcNow(),9)}",
                "definition": "@body('JSON_の解析')?['definition']",
                "state": "Stopped"
            }
        },
        "path": "/environments/@{encodeURIComponent('XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX')}/flows",
        "authentication": {
            "type": "Raw",
            "value": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']"
        }
    }
}
```
</div></details>

最後にフローを保存をしたら完成です。

### フロー全体図
![2019-03-17-17_45_52.png](https://qiita-image-store.s3.amazonaws.com/0/258416/75c8482e-a875-ab06-054c-ee72fe3ef901.png)


### ワークフロー定義（分かるひとには読むだけで分かるやつ）

||行|値|
|---|---|---|
|$1|96|インポート先の環境名|

```json-doc
{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "$connections": {
        "defaultValue": {},
        "type": "Object"
      },
      "$authentication": {
        "defaultValue": {},
        "type": "SecureObject"
      }
    },
    "triggers": {
      "manual": {
        "type": "Request",
        "kind": "Button",
        "inputs": {
          "schema": {
            "type": "object",
            "required": [
              "text"
            ],
            "properties": {
              "text": {
                "type": "string",
                "x-ms-dynamically-added": true,
                "description": "入力を指定してください",
                "title": "入力",
                "x-ms-content-hint": "TEXT"
              }
            }
          }
        }
      }
    },
    "actions": {
      "Create_Flow": {
        "runAfter": {
          "JSON_の解析": [
            "Succeeded"
          ]
        },
        "metadata": {
          "flowSystemMetadata": {
            "swaggerOperationId": "CreateFlow"
          }
        },
        "type": "ApiConnection",
        "inputs": {
          "host": {
            "connection": {
              "name": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$connections']['flowmanagement']['connectionId']"
            }
          },
          "method": "post",
          "body": {
            "properties": {
              "displayName": "imported_@{addHours(utcNow(),9)}",
              "definition": "@body('JSON_の解析')?['definition']",
              "state": "Stopped"
            }
          },
          "path": "/environments/@{encodeURIComponent('$1')}/flows",
          "authentication": {
            "type": "Raw",
            "value": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']"
          }
        }
      },
      "JSON_の解析": {
        "runAfter": {},
        "type": "ParseJson",
        "inputs": {
          "content": "@triggerBody()['text']",
          "schema": {
            "type": "object",
            "properties": {
              "definition": {
                "type": "object",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "outputs": {}
  }
}
```

### 動作確認

たった今GUIからポチポチ作成したインポート用フローを、上記のワークフロー定義を基に再度作成してみます。

まず、上記ワークフロー定義をどこかのテキストエディタにコピペし、$1(96行目)にお使いのフロー環境の環境名（'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'のような形式）を指定します。

インポート用フロー実行画面で[入力]に環境名入りのワークフロー定義をコピペし、[フローの実行]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/fae42e08-f598-97f0-7071-b1cb7dbb32ce.png)

フローが正常に実行され、指定した環境にフローが作成されていることを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/947c6cfb-26eb-614b-7afe-45a65fa43798.png)

しかしフロー編集画面を開くと・・・
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/6c08f13e-23e6-5188-847d-11a0f8af024e.png)

</div></details>

# おわりに

[Create Flow]アクションの代案として、zip形式でフローをエクスポートした場合はzip内に環境固有の値が含まれてしまうため、zip解凍→固有値をマスクして取り除く→再zipした上でzip形式でインポートする方法も試しましたが、どうしてもインポート時にエラーとなってしまい、代案とはなりませんでした。

Microsoft Flow においてワークフロー定義が Docker における Dockerfile のような役割を果たしてくれると期待していただけに、項番３を完全な形で実現する方法がいまのところないというのは非常に残念でした。

ただし、項番１、２においてはワークフロー定義の利用が有用であることは変わりないため、参考にして頂ければと思います。

また、どなたか項番３を実現する妙案をお持ちでしたらぜひ教えて頂きたいです。

以上