パスワード有効期限切れ間近のドメインユーザにメール通知する【PowerShell】

こんにちわ、ますのです。
コロナ対策でリモートワーク推奨が一気に加速しましたね。その影響でVPNネットワークが逼迫したり、普段は負荷が少ないネットワーク機器がアラート上げたりと、拡張性を考えた設計って大事…。と思う機会にぶち当たっております。

障害こそ勉強の機会なり^q^

さて本題、パソコンの立ち上げ時にドメインネットワーク接続できていないことが原因で、OS機能のパスワード更新通知が表示されない状況がコロナにより作り出されてしまいました。まぁ、、、一般利用者なんて、、、パスワード期限なんて、、、気にしないわけで。。。。
ユーザからは「コロナですよ!?出社するのなんてありえない!!」って言われてしまったので、気付いてもらえるようにメール通知出来ないかと突貫で作った次第であります。

筆者は今回、コピペ以外でPowerShellを自作するのは初めてであります!
色々情報集めてつぎはぎで作ったので、不格好なコードでも生暖かい目で見てほしいであります!

メール通知PowerShell:仕様紹介(簡易)

大まかな流れとしてこんなPowerShellになっています。

  • 対象:ActiveDirectoryに登録されているドメインユーザーアカウント
  • パスワード有効期限が切れるドメインユーザに、7日前から0日になるまで毎日1回メールを通知する
  • 既に有効期限が切れているドメインユーザには送らない
  • 無期限設定しているアカウントも対象としている(←改良の余地あり)
  1. 実行するフォルダパスを指定する
  2. メール送信を行ったユーザ一覧を「.\log」フォルダに作成する
  3. 送信リストの世代管理を7日間保持、7日経過したデータは削除する
  4. ドメインポリシーで設定しているパスワード有効期間の日付:42日を設定する
  5. パスワード期限切れユーザを確認したいOUを指定する
  6. 5のユーザで「パスワードを1回でも変更したユーザ」のみをループ対象とする
  7. 6のユーザを1人ずつ、「今日の日付ー変更日」で差分の日数を計算する
  8. 7のユーザで「残り有効期限:0日~7日」のユーザをリストに追記
  9. 7のユーザに対して期限が近いよという内容をメールで通知する
  10. 以降「6~9」をOU内全て対象が終わるまで確認する

本当は有効期限日を抽出したかったのですが項目として入力されていても日付形式になっておらず、変換を加えようとオプションを加えると空白値が返ってきてしまうので今回は早さ重視で出来ることから実施しました。

パスワード期限切れ間近のドメインユーザにメール通知を行うPowerShell

#--------▼スクリプト保存フォルダを指定▼-------
$filedir = "c:\pw-notifications\"

#--------▼ログファイル設定▼------------------
#logファイル名&保存先設定
$logdate = Get-Date -Format "yyyy-MMdd-HHmmss"
$logfile = $filedir + "\log\SendList_" + $logdate +".csv"

#logローテーション
$DeleteFile = $filedir + "\log\SendList*.csv"
$DAYS = 7
Get-ChildItem $DeleteFile -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-1 * $DAYS))} | Remove-Item -recurse -force

#CSV出力のテンプレートデータを新規作成
Write-Output "sAMAccountName,mail,TimeLimit,PasswordLastSet,ExpirationDate" | Add-Content $logfile -Encoding UTF8

#--------▼メール共通設定▼-----------------
# 送信元メールアドレス
$from = "password-notifications@aaaa.com"

# SMTPサーバー:必要に応じてポートや認証情報等は追記する
$smtp = "smtpサーバ指定"

#--------▼リスト処理用設定▼--------------
#パスワード有効期間指定
$PWlimit = New-TimeSpan -days 42
$today = get-date

#ユーザリスト取得範囲(OUを指定する)
$GetUser = Get-ADUser -Filter * -SearchBase "OU=users,DC=soypocket,DC=com" -Properties mail,PasswordLastSet  | select sAMAccountName,mail,PasswordLastSet

#-----------処理開始------------
$i=1
foreach ($UserList in $GetUser) {
#foreach ($UserList in Get-Content .\pw-limit-test.csv|ConvertFrom-CSV) {


#----パスワードが1回でも更新されているユーザを対象とする
$str1 = $UserList[0].PasswordLastSet
if ($str1) {

    #----パスワード期限切れまでの日数を算出
    $duration = New-TimeSpan $UserList[0].PasswordLastSet $today
    $TimeLimit  = $PWlimit.Days - $duration.Days
    $TimeLimit = $TimeLimit -1

    #----パスワード期限が残り7日以下、0日以上のユーザを対象とする
	if($TimeLimit -le 7 -and $TimeLimit -ge 0 ){

    #----対象ユーザをCSVデータに追記するための事前準備
	  	$sAMAccountName = $UserList[0].sAMAccountName
  		$mail = $UserList[0].mail
  		$PasswordLastSet = $UserList[0].PasswordLastSet

    #----有効期限日の算出
      $ExpirationDate = $(Get-ADUser -Identity $sAMAccountName -Properties passwordlastset).passwordlastset.adddays($PWlimit.Days)
      [string]$ExpirationDate = $(Get-ADUser -Identity $sAMAccountName -Properties passwordlastset).passwordlastset.adddays($PWlimit.Days)

   #----有効期限日の算出後に日本表記に変更する処理をする(メール記載のため)
      $ExpirationDate = $ExpirationDate.substring(6,4) + "/" + $ExpirationDate.substring(0,2) + "/" + $ExpirationDate.substring(3,2) + $ExpirationDate.substring(10)

   #----CSVデータにメール送信対象ユーザを追記する
      $strOutput = $sAMAccountName + "," + $mail + "," + $TimeLimit + "," + $PasswordLastSet + "," +$ExpirationDate
	    Write-Output $strOutput | Add-Content $logfile -Encoding UTF8

#----メール文面等の設定
        # 件名
        $subject = "パスワード有効期限のお知らせ【残り $($TimeLimit) 日】"

        # 本文(変数以外の部分はmail-body.txtから取得)
        $body = "$($UserList[0].sAMAccountName)さん`r`n`r`n" 
        $body += "パスワードの有効期限があと $($TimeLimit) 日となりました。 `r`n"
        $body += "パスワード有効期限: $($ExpirationDate) `r`n"
        $body += (Get-Content $filedir\mail-body.txt) -join "`n"

        # 宛先設定
	    	$to= $UserList[0].mail
        # メールを送信します
        Send-MailMessage -To $to -Bcc $bcc -From $from -SmtpServer $smtp -Subject $subject -Body $body -Encoding UTF8
	}
 }
 $i++
}

メール本文をGet-Contentでもってきているのは、長文入れたいので追記しました。
変数をGet-Contentのファイル内に入れてもそのまま文字列で出てきてしまうので、泣く泣くこの形式に。

色々改良の余地がありそうなところですが、いったんは無事リリースしたのでこれで行こうと思うのです。

パスワード有効期限が切れるドメインユーザへのメール通知に関して参考にしたものたち

参考にさせていただいたサイト一覧 備考
Active Directory ユーザーにパスワード更新期限をメール通知する
ドメインユーザのパスワード有効期限更新通知を積極的に促す
事の発端。このコードを試すもうまくいかなかったので自作に走る。
PowerShellでパスワード有効期限切れ間近のOffice365ユーザーを出力する ベースとなったコード。
Office365のコマンドからAD用のコマンドに変更。処理の流れは完全にこちらのサイトに寄せてます。
Windowsローカルユーザーのパスワード有効期限確認用PowerShellスクリプト 「ロックされているユーザは除外する」という部分も応用出来そう(今後の話)
【PowerShell】何日以前(指定した日数)のファイルを削除する方法 ログローテーションの使い方でお世話になりました。
Powershellの時間計算すごい New-TimeSpanの使い方の参考に
【Powershell】文字列が null であるか判定するスクリプト $UserList[0].PasswordLastSetの分岐でnull値かどうかを判別する時の参考に
‘op_Addition’ という名前のメソッドが含まれないため、メソッドの呼び出しに失敗しました。 配列のループ処理で失敗した時に参考にした気がする。
あんまり覚えてないのです。。。
Windows PowerShellの文字列内で変数を扱う方法 メール文面に変数をどうしても使いたくて参考に
Windows(PowerShell)でコマンドを使ってメールを送信する方法
PowerShellでメール送信
メール送信方法の参考に(認証等の細かいところ)
Powershellでメールを送信する最も簡単な方法 メール送信方法の参考に(最少要件の確認)
Powershellでテキストファイルの中身をメール送信する メール送信時にテキストファイルをインポートする方法の参考に

Twitterでも助けていただきました!@kaba_kichiさんありがとうございました!

 

今回初めてPowerShellで作った状態ですが無事にリリースできました!
これで少しでも問い合わせが減ればいいなぁ…と願う所存であります。

「function」の使い方とかも覚えると分岐処理で色々使えそうだなぁと感じたので、また作る機会があればやってみたいと思います。

最新情報をチェックしよう!