Office365 の共有メールボックスから Powershell で送信する

# はじめに
Office365 のアカウントから Powershell を使ってメールを送信する記事は多く見つかるのですが、共有メールボックスから送信する記事は見つからなかったので作成しました。
ついでに設定を Script に直に記述しなくても簡単に送信元を切り替えられるよう、設定ファイルを XML で外だししました。(こっちがメインになってしまったかも)

ポイントは下記2点です。

- 認証するユーザと送信元アドレスを個別設定している
- 前述の設定ファイルの分離

分離された設定ファイルはデフォルト値を予め設定しているので、引数 (ConfigXml オプション) が指定されていない場合は Script と同一ディレクトリの XML ファイルを呼びに行きます。

余談ですが、最初 Qiita に投稿するつもりで書いていたのですが、はてなブログに載せたらどう見えるかなと思ってはてなブログにも投稿してみました。
はてなブログでもコードをハイライトできることを発見し、深夜に一人「できる、できるぞー」と心の中で叫んでしまいましたw
というか Qiita って予約投稿できないんですね。。。ちょっと残念。ということでこちらは手動投稿です。


# 前提条件
- ExecutionPolicy が RemoteSigned になっている
- 送信元となる Script 実行ホストから Port587 で Office365 の SMTP サーバに接続可能な状態
- Exchange Online 側で SMTP が有効になっている


# 動作確認済み環境
| OS | $PSVersionTable の PSVersion |
|---|---|
| Windows Server 2012R2 | 4.0 |


# 実行例
設定ファイルを指定しない場合 ( デフォルト設定の XML を呼び出し )

```
PS C:\Windows\system32>  C:\Script\SendEmailScript.ps1
```

設定ファイルを指定する場合 ( 指定の XML を呼び出し )

```
PS C:\Windows\system32>  C:\Script\SendEmailScript.ps1 -ConfigXml "C:\Script\SendEmailScript1.xml"
```


# Powershell Script
``` SendEmailScript.ps1
<#
    Script Name                : Send E-Mail Script
    Script File                : C:\Script\SendEmailScript\SendEmailScript.ps1
    Default Configuration File : C:\Script\SendEmailScript\SendEmailScript.xml
    Script Version             : 1.0
    ExectionPolicy             : RemoteSigned
    LastUpdate                 : 2019/01/27

    History :
      2019/01/27 Create New
#>

Param(
    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [String] $ConfigXml = ((Split-Path $myInvocation.MyCommand.path) + '\SendEmailScript.xml')
)

# Check Configuration File
If (!(Test-Path -Path $ConfigXml)) {
    Write-Host ("Configuration file not found.")
    Exit
}

# SendEmail Function
function EmailPreparation {
    # Read configuration file
    foreach ($Param in ([xml](Get-Content $ConfigXml)).Config) {
        # Set Cred Info
        $CredUser           = ($Param.Cred.CredUser)
        $CredPwd            = ($Param.Cred.CredPwd)

        # Set Mail Setting
        $SmtpServer         = ($Param.Mail.SmtpServer)
        $SmtpPort           = ($Param.Mail.SmtpPort)
        $FromAddr           = ($Param.Mail.FromAddr)
        $DestAddr           = ($Param.Mail.DestAddr)
        $MailSubject        = ((Get-Date -format 'yyyyMMdd-HHmmdd') + " " + ($Param.Mail.MailSubject))
        $MailBodyFilePath   = (Get-Content -Encoding Ascii ($Param.Mail.MailBodyFilePath) -Raw)
    }

    # Create Stmp Client
    $SmtpClient             = New-Object Net.Mail.SmtpClient($SmtpServer,[int]$SmtpPort)
    $SmtpClient.EnableSsl   = $true
    $SmtpClient.Credentials = New-Object System.Net.NetworkCredential($CredUser, $CredPwd)

    # Send Mail Processing
    $SmtpClient.Send($FromAddr, $DestAddr, $MailSubject, $MailBodyFilePath)
    $SmtpClient.Dispose()

}

# Write Error Log Function
function WriteErrorLog ($ErrorMsg) {
    foreach ($Param in ([xml](Get-Content $ConfigXml)).Config) {
        # Set Env
        $ErrorLogDirPath    = ($Param.Env.ErrorLogDirPath)
        $ErrorLogFileName   = ($Param.Env.ErrorLogFileName)
        $ErrorLog           = ($ErrorLogDirPath + "\" + $ErrorLogFileName)
    }
    If (!(Test-Path -Path $ErrorLogDirPath)){
        New-Item -Path $ErrorLogDirPath -ItemType Directory -Force
        If (!(Test-Path -Path $ErrorLog)){
            New-Item -Path $ErrorLog -ItemType File -Force
        }
    }
    Add-Content $ErrorLog ((Get-Date -Format "yyyy/MM/dd HH:mm:dd:ss") + " : " + $ErrorMsg)
}


# Send Email
try{
    EmailPreparation
}
catch{
    WriteErrorLog("Failed to send e-mail.")
}
```

# XML
設定する項目は以下の通りです。サンプル XML を改版する際の参考にしてください。

| 設定項目 | 設定内容 | 備考 |
| --- | --- | --- |
| ErrorLogDirPath | ログファイル格納先のパス | |
| ErrorLogFileName | ログファイルのファイル名 | |
| CredUser | 認証ユーザ | |
| CredPwd | 認証ユーザのパスワード | |
| SmtpServer | SMTP サーバ | IP か FQDN で指定 |
| SmtpPort | SMTP のポート番号 | OB25P で 587 を指定するケースが多いはず |
| FromAddr | 送信者のメールアドレス | |
| DestAddr | 宛先のメールアドレス | 複数の宛先にする場合はコンマで区切る |
| MailSubject | メールのタイトルに入れる定型文 | 不要なら空欄で OK |
| MailBodyFilePath | メール本文に入れるテキストファイルのパス | 下記サンプル XML では hosts ファイルを指定しています |

``` SendEmailScript.xml
<?xml version="1.0"?>
<Config>
  <Env>
    <ErrorLogDirPath>C:\Scripts\Logs</ErrorLogDirPath>
    <ErrorLogFileName>SendEmailError.log</ErrorLogFileName>
  </Env>
  <Cred>
    <CredUser>user01@hogehoge.com</CredUser>
    <CredPwd>password</CredPwd>
  </Cred>
  <Mail>
    <SmtpServer>smtp.office365.com</SmtpServer>
    <SmtpPort>587</SmtpPort>
    <FromAddr>info@hogehoge.com</FromAddr>
    <DestAddr>user01@foofoo.net,user02@foofoo.net</DestAddr>
    <MailSubject>Send e-mail test</MailSubject>
    <MailBodyFilePath>C:\Windows\System32\Drivers\etc\hosts</MailBodyFilePath>
  </Mail>
</Config>
```