CDOでMicrosoft365メールが使えなくなったので調べてみた

**CDO**（Microsoft Collaboration Data Objects）を使ったメールの一括送信Excelマクロが、転送エラーを返すようになった、とのこと。
メールの送信には Microsoft365 を使っています。
![転送エラー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/543d68f4-2e98-fb5a-5efc-c9463a37827c.png)
結局、解決には至りませんでしたが、誰かの役に立てればと調べた結果を共有します。

:::note warn
**追記**
解決しました。Exchangeオンラインで設定変更後、特定の環境からポート25で接続に成功しました。
:::

# やったこと
PowerShellからExchangeオンラインに接続します。

```powershell
Connect-ExchangeOnline -UserPrincipalName foo@example.com -ShowProgress $true
```

この後、パスワードやワンタイムパスコードを入力します。
`Get-InstalledModule`の結果に`ExchangeOnlineManagement`が無ければ、事前にモジュールを有効化しましょう。

```powershell
Install-Module -Name ExchangeOnlineManagement -Force
Import-Module ExchangeOnlineManagement
```

設定を確認します。

```powershell
Get-CASMailbox -Identity bar@example.com
```

制限されていれば解除します。

```powershell
Set-CASMailbox -Identity bar@example.com -ImapEnabled $true -PopEnabled $true -MapiEnabled $true -ActiveSyncEnabled $true
```

SMTP AUTH も有効にしておきます。

```powershell
Set-CASMailbox -Identity bar@example.com -SmtpClientAuthenticationDisabled $false
```

なお、$null を指定するとテナントのグローバル設定に従います。
テナントのグローバル設定を確認するには、

```powershell
Get-TransportConfig | Format-List SmtpClientAuthenticationDisabled
```

グローバルIPアドレスによる接続制限をかけられていないかも確認しておきます。
何も表示されなければ個別の定義はありません。

```powershell
Get-ClientAccessRule
```

これらの設定確認および変更は、[Exchange管理センター](https://admin.exchange.microsoft.com
)からも一部できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1122be33-ef4f-a9c7-f60b-a3576e26c2a6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/c16daf5a-f5f1-c21d-deaf-5caaacfa2625.png)
テナントで基本認証がブロックされていないことも確認しておきます。
[Microsoft管理センター](https://admin.microsoft.com) [設定]－[組織設定] から先進認証をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/8c543607-58ae-ea08-caeb-f004b9475220.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/84824ccc-5b6b-06bb-183b-d1f326fa40ae.png)
なお、**先進認証**（*Modern Authentication*：モダン認証とかADAL認証とも呼ばれます）とは、HTTPSで**アクセストークン**（アクセス許可、権限範囲、有効期限が示されたチケットのようなもの）をサーバに送信する方式のことです。これに対し、**基本認証**（*Basic authentication*：ベーシック認証とかレガシー認証とも呼ばれます）は、ユーザ名とパスワードをサーバに送信します。

# しかし・・・
状況は改善されません。
CDOで指示するポート番号を 25、465（SSL/SMTPS）、587（TLS/STARTTLS 要SMTP AUTH）と変えてみてもダメでした。

外部のサーバから`telnet`で直接接続を試みます。

```console
$ telnet smtp.office365.com 25
Trying xxx.xxx.xxx.xxx...
Trying yyy.yyy.yyy.yyy...
  :
telnet: Unable to connect to remote host: Connection timed out
```

できませんでした。認証／認可の前に繋がらないのはおかしいです。
`Connection refused`（接続拒否）が返ってこないので、サーバでポートがリッスンしていないわけでは無いことが判ります。
このときにパケットをキャプチャしてみると、SYNパケットを繰り返し送信するだけで、相手から何の応答もありません。これはどこかでパケットを遮断していると判ります。（サーバが拒否しているならSYNに対してRSTを応答するはず）

```console
# tcpdump port 25
07:53:50.271175 IP6 XXXXX > YYYYY.smtp: Flags [S], seq 2530573894, win 62587, options [mss 8941,sackOK,TS val 1414108762 ecr 0,nop,wscale 6], length 0
07:53:50.271175 IP6 XXXXX > YYYYY.smtp: Flags [S], seq 2530573894, win 62587, options [mss 8941,sackOK,TS val 1414108762 ecr 0,nop,wscale 6], length 0
  :
```

経路をみてみます。相手方のネットワークまでは到達しているようです。

```console
# mtr smtp.office365.com -P 25
                                                                     My traceroute  [v0.92]
example.com (xxx.xxx.xxx.xxx)                                                                                                           2022-11-02T22:33:22+0900
Keys:  Help   Display mode   Restart statistics   Order of fields   quit
                                                                                                                        Packets               Pings
 Host                                                                                                                 Loss%   Snt   Last   Avg  Best  Wrst StDev
 1. ec2-yyy-yyy-yyy-yyy.ap-northeast-1.compute.amazonaws.com                                                           0.0%    14    5.0  14.1   1.0  76.4  24.1
      :
      :
22. zzzzz.icr02.tyo30.ntwk.msn.net                                                                                     0.0%    13    4.1   4.4   3.6   7.6   1.1
      :
      :
28. 40.99.36.zzz                                                                                                       0.0%    13    2.9   2.9   2.9   3.0   0.0
```




TLS/SSL接続ではどうでしょう。`openssl`で試してみます。
SMTP AUTH ではユーザー名とパスワードをbase64でエンコードした文字列を渡すことになるので予め求めておきましょう。

```console
$ printf 'user@example.com' | base64
$ printf 'password' | base64
```

smtp.office365.com では`-crlf`を付けないとうまくいきませんでした。

```console
$ openssl s_client -connect smtp.office365.com:587 -quiet -crlf -starttls smtp
```

サーバとのやり取りを示します。
4文字の英字で始まる行はクライアントから入力したSMTPのコマンド、3桁の数字で始まる行はサーバからの応答です。
AUTHコマンドの後に続くXXXXXXがbase64エンコードしたユーザー名、YYYYYYがbase64エンコードしたパスワードになります。
HELOコマンドではなく拡張されたEHLOコマンドを使うことで、SMTPサーバがどんな認証方法に対応しているか知ることができます。

```conf
EHLO localhost
250-TYWPR01CA0023.outlook.office365.com Hello [xxx.xxx.xxx.xxx]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH LOGIN XOAUTH2
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
AUTH LOGIN
334 VXNlcm5hbWU6
XXXXXX
334 UGFzc3dvcmQ6
YYYYYY
235 2.7.0 Authentication successful
MAIL FROM: foo@example.com
250 2.1.0 Sender OK
RCPT TO: bar@example.co.jp
250 2.1.5 Recipient OK
DATA
354 Start mail input; end with <CRLF>.<CRLF>
From: foo@example.com
Subject: TEST
Hello world
.
250 2.0.0 OK <zzzzzzzz.jpnprd01.prod.outlook.com> [Hostname=zzzzzzzz.jpnprd01.prod.outlook.com]
QUIT
221 2.0.0 Service closing transmission channel
```

接続に成功し、認証方式は *SMTP AUTH LOGIN* と *XOAUTH2* に対応していることが判ります。

さらに調べていくと、海外のブログにヒントが。

https://www.tachytelic.net/2018/12/email-vbscript-vba-office-365/

昨年末あたりからTLS接続が必須になり、従来のSSL接続をサーバが拒否するようになった、と書かれています。
その一方で、CDOにSSL接続を指示すると実際にはSSLの後継であるTLSが裏で動いている、というコメントも寄せられています。

テナントによって挙動が違うのは、10月1日以降、Microsoftがテナントをランダムに選択して、基本認証（ここではSMTP AUTH以外のこと）のアクセスを無効にしたことも関係しているのでしょうか。こちらのコミュニティにはそのようなことが書かれています。

https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-deprecation-in-exchange-online-september/ba-p/3609437

VPNで社内環境に接続したら送信できた、という事例も見つけました。（グローバルIPアドレスをチェックしている？）

https://www.1josys.com/archives/38212996.html

:::note
**追記**
最終的にはこの情報が解決の糸口になりました。
これまでは自宅で調査していたのですが、社内から接続したところ、あっさりと成功しました。
:::

# 結論
Microsoftは今後、Exchangeオンラインの SMTP AUTH も無効にする（OAuthベースの先進認証に移行していく）と考えられるので、CDOを使った方法はもう諦めた方が良さそうです。

先述と同じ人のブログですが、ノーコードツールの Power Automate でGoogleスプレッドシートと連携し、Microsoft365メールを一括で送信していました。これは素晴らしいですね！

https://www.1josys.com/archives/38279204.html

# 参考ドキュメント
https://learn.microsoft.com/ja-jp/exchange/clients-and-mobile-in-exchange-online/clients-and-mobile-in-exchange-online