イベントやメルマガ購読のためのメールアドレス収集を Forms とPower Automate で実現する

# はじめに

このフローは、ありがちなメールアドレスの確認フローです。
登録フォームに必要事項を記入しますが、その中に自分のメールアドレスも入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/aee3cd68-c526-260e-c047-2961f7cc564c.png)

フォームを送信すると、記入したアドレスにURLリンク付きのメールが届きます。
リンクをクリックすると、メールアドレスの確認が完了となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/f3a0d00d-98f6-f54f-e984-f03ed0dccf07.png)

裏側では、フォームから登録してくれた人や、その登録状況の一覧を管理しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ebaf2d4d-dd2e-0315-56cd-4541c90a74ab.png)


:::note warn
**注意**
このフローではプレミアムコネクタ(有償)を使用します。
:::

また、Power Pages でのユーザ認証機能を使う方がシンプルだと思います。
どうしても、そこまではやりたくない小規模なイベントなどにご活用ください。

# このフローが必要な理由
Formsでメールアドレスを収集するには、テキスト形式の質問を使用します。
しかし、この質問にかけられる制限は、数値としての制限のみになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/cf59e56c-3796-0507-3526-dc23645fd46e.png)

そのため、メールアドレスとして正しい形式になっていなくても、そのまま登録できてしまうという欠点があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/00092418-4523-01ac-ce59-f16397661c5a.png)

また、間違えの無いように、メールアドレスを2回記入してもらう方法もありますが、Forms内ではこの2つの入力が同じものになっているかというチェックもできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/f3aeb60a-bd25-2d24-ec5c-39264ec80e57.png)

このように、Formsだけで正しいメールアドレスを収集するのは、かなり難しいです。
正しくメールアドレスが入力できているか？のチェックは、入力側への負担を強いることになります。

# 使用するツール
このフローでは以下のアプリ・機能を使用します。

|ツール|用途|
|:-|:-|
|Microsoft Forms|登録の受付|
|Outlook|確認メールの送信。(※メールが送れるのであれば、ほかのメーラーでもよい)|
|SharePoint List|登録者・登録状況の管理|
|Power Automate|各ツールの連携|


# フローの概要
この一連の仕組みには、2つのPower Automate フローを使用します。

1つはFormsの応答をトリガーにして、入力されたアドレスに確認メールを送信するフロー。
もう1つはメールに記載されたリンクのクリックを検知して、登録を完了させるフローです。

![2024-02-08_22h44_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/69d9d652-c9fa-5ecc-cfbf-29e0a9559e2c.png)

途中で入力者のリンクをクリックするという、非同期的なトリガーを挟むため、1つのフローで達成するのは難しく、2つのフローに分かれています。

<details><summary>フローの全体像①Forms受入</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ad5b7d07-bf70-7947-2b10-09e7f2136a61.png)

</details>

<details><summary>フローの全体像②URLクリック確認</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/8829265b-9aeb-c867-8798-21ab043509bc.png)


</details>

---

ここからフローの解説をしていきますが、説明の都合上、2つのフローを行き来することになります。予めご了承ください。

# フローの解説①Forms受入：前半部分

Formsの受付、SharePointリストへの転記など、基本的なところは割愛して解説します。

## GUIDの生成
メールアドレスの確認のためにランダムな文字列を生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/65517fba-6375-8c02-6e02-4a3be9839322.png)

https://learn.microsoft.com/ja-jp/azure/logic-apps/workflow-definition-language-functions-reference#guid

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/a9193d8b-2cd0-4b72-e3b6-866239924d7c.png)


要は、本人確認のためのパスワードを生成する感じです。
これがないと **「メールアドレスさえ知っていれば、他人のメールアドレスで登録できてしまう」** といったことになりかねません。

作成したGUIDは、SharePointリストにも登録しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/65d95de4-7fe6-4cdd-e1d7-36f0f4870cb6.png)



# フローの解説②URLクリック確認：前半部分
1番目のフロー解説の途中でしたが、2番目の解説に移ります。

## 使用するトリガー
トリガーには、組み込みの`HTTP要求の受信時`トリガーを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/9f09cbea-167f-0c5f-53bb-2596a05a1ae4.png)

設定が必要な項目は2箇所です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/28a17752-ab91-9897-1278-fa312c92e2a3.png)

このトリガーと、少なくとも1つのアクション(このときはなんでもよい)を追加してフローを保存します。

フローを保存すると、保存して初めてURLが生成されます。
このURLを①のフローで使用する必要があったので、説明が前後しましたが、このトリガーの説明を先に行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ad23c998-2bf2-a0ae-ebf7-64aa842b321b.png)

このURLをコピーしておいて、①のフローに戻りましょう。

# フローの解説①Forms受入：後半部分
GUIDを生成した後の①のフローに戻ります。

## 確認用URLの初期化
先ほどコピーしておいたURLを、文字列変数に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/70484248-94c5-725a-9929-cb0cbe3938ea.png)

このURLは確認用メールに記載するURLのもとになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ef114d64-317f-6941-31b1-fec4dacedf19.png)

## 確認用URLに情報を追加
先ほどのURLをよく見ると、メールアドレスとGUIDの情報が追加されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/11eec9ec-fc79-540d-914b-2330ac443dd2.png)

この情報をURLに追加しておくことで、どのメールアドレス宛に送ったURLなのか？ということや、パスワードが正しいか？ということを確認できるようになっています。

先ほど初期化した変数のURLに、文字列を追加してつなげていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/f2eaee56-122a-08cd-8ce6-853d89e4cb5f.png)

`&mail=`の後にFormsで受け取ったメールアドレスを、`&guid=`の後に生成しておいたGUIDというパスワードを追加します。
```plaintext:文字列変数に追加
&mail=@{uriComponent(outputs('応答の詳細を取得する')?['body/{アドレス確認の質問ID}'])}&guid=@{variables('GUID')}
```

この`uriComponent`という関数は、日本語などのURLに入力できない文字を変換してくれる関数です。

https://learn.microsoft.com/ja-jp/azure/logic-apps/workflow-definition-language-functions-reference#uriComponent

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/b154e8cb-cad0-d88c-a22b-73157fdf2e73.png)


## メールの送信
URLの作成が終わったので、作成したURLを記載したメールを送信して、1番目のフローは完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ef114d64-317f-6941-31b1-fec4dacedf19.png)

ただし、メールアドレスが間違っていて送信できない可能性を考慮しておく必要があります。（Formsがチェックしてくれないので）

そのため、エラー処理として、メール送信に失敗した場合のアクションを追加しておきます。

メールがうまく遅れなかった場合のみ、登録状況を、**送信が失敗したステータス**に切り替えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/330775a6-00d4-a5f0-5833-d4cf0491f884.png)

このアクションが実行されるのは、**前のアクションが失敗した時だけ**にしておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ea70229d-657c-14f3-f4cf-ae7d036c0d13.png)

これで1つ目のフローは完成です。

# フローの解説②URLクリック確認：後半部分
メールで送ったURLにアクセスされたときに、このフローは動きます。

## アドレスとGUIDの確認
URLに追加した、メールアドレスとGUIDの情報を取り出します。
追加した情報はクエリパラメータとして取り出せるので、以下の方法を使用して変数に格納します。

https://mofumofupower.hatenablog.com/entry/2020/04/22/103242


![2024-02-11_01h24_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/bb240e94-eed0-3c46-def9-bb516280c122.png)
```plaintext:guid
triggerOutputs()['queries']?['guid']
```
![2024-02-11_01h25_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/d8adbe08-60f7-b1a5-4c28-db8beb2b18f5.png)
```plaintext:mail
triggerOutputs()['queries']?['mail']
```

## 登録状況の確認
取り出した2つの情報をもとに、登録情報の一覧から該当する行を探し出します。

`複数の項目の取得`アクションのフィルタークエリを使って、メールアドレスとGUIDが取り出した情報と一致する行を抽出します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/11fa2d63-33f8-57c9-d02d-2bfd6ba99bec.png)

```plaintext:フィルタークエリ
(guid0 eq '@{variables('guid')}') and (mailAddress eq '@{variables('mail')}')
```

フィルタークエリは、旧デザイナーの方がGUIで設定できるので、こちらの方がわかりやすいかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/8707a16a-61fa-5305-64e0-c46feee783c1.png)

## 状況の確認
先ほどの抽出で、以下の場合には登録を進められないため、条件分岐を使って確認していきます。
- アドレス・GUIDが一致する行がない場合
- 一致する行はあったが、すでに登録されている場合(間違って2回クリックした場合など)
- 一致する行はあったが、送信エラーなど、登録待ちのステータスではなかった場合

### 除外条件①一致する行がない
一致するものがあったかなかったかは、抽出するアクションの結果が`空`かどうかで判定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/bce33f92-6fed-b1f8-bde8-c8f891101253.png)


### 除外条件②ステータスがおかしい
登録ステータスの確認は、`スイッチ`分岐で確認しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/0594fca5-95c3-2ad4-e26c-2ea1971b433c.png)


## リンクをクリックしたときの表示内容の設定

先の条件ごとに、表示する内容を決めておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/d7c27c0f-12ec-3d85-de4a-3d447f9f53ff.png)

先に中身が空の文字列変数を定義しておいて、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/68e92cc5-7097-ba7a-a8ca-87758618c739.png)

それぞれの条件で内容を設定するとよいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/c9068188-37b1-1f3f-5f61-f1ae92870ea8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/87a30268-53ac-a285-dd7b-bbe921b8b9b2.png)

正しく登録が完了した場合は、メッセージの定義だけでなく、表のステータスを更新するのも忘れずに設定しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/0cb040b8-9138-2894-ccae-5bb367d4a461.png)


## HTMLページの作成
先ほどの文章が表示できるようにHTML(Webページ)を作成します。
自分で書けなくても最近は生成AI に手伝ってもらえるので便利ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/8e07c580-30ed-3a78-296b-cccb6fc57562.png)

今回はこんな感じにしておきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/48304272-f412-f032-a19e-2735f647b912.png)
```html:作成
<!DOCTYPE html>
<html>
<head>
  <style>
    .center {
      text-align: center;
      vertical-align: middle;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  </style>
</head>
<body>
  <div class="center">
    <p>@{variables('response')}</p>
  </div>
</body>
</html>
```

## 応答
最終的にそのページを表示させるために、リンクをクリックした人に情報を返します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/e1f349d2-2b91-29b5-7461-eaa1861cc5d4.png)

ヘッダーに「HTMLで返しますよ」という宣言と、ボディにさっき作ったHTMLの文字列データそのものを渡せばOKです。
```json:Header
{
  "content-type" : "text/html;charset=utf-8"
}
```

```plaintext:Body
@outputs('作成')
```

これでフローが完成です。

# おわりに
M365をご利用の環境であれば、追加でPower Automate Premium ライセンスを用意することでこのフローが使用できます。

イベントごとにサイトを作るよりは、このフローを作成する方が簡単でしょうか？
個人的にはフローに慣れているんで楽でしたが、APIやHTMLなど、様々な周辺知識を要求するため、人によっては難しいかもしれません。

目的に適した手段をご利用ください。