PowerShell が私の中で最近急上昇株になっています。そんなわけで、Active Directory に接続して、ユーザを特定の条件を元に検索し、該当したユーザのプロパティを設定するサンプルを掲載します。

検索条件として特定に日時よりパスワード変更をしていないユーザを対象とします。対象ユーザに次回ログイン時にパスワードの変更が必要を設定します。

動作確認環境

  • Windows 2003 DC 上
  • PowerShell 1.0

1. サンプル

パスワードを最後に変更した日時 < 2009/06/30 00:00:00 となる、特定のOU(組織)配下のユーザを検索し、次回パスワード変更が必要をセットします。ただし、パスワードが無期限の場合は、次回パスワード変更が必要をセットしないようにしています。

下記サンプルをps1ファイルに保存して、PowerShellから実行する場合は、ExecutionPolicy を RemoteSigned にする必要があります。現在の実行ポリシーはGet-ExecutionPolicy で確認できます。

サンプルで設定しているように、PageSizeと、SizeLimit を指定しないと、最大1000件までしかフィルタ条件に該当するユーザが検索されないので、注意してください。

#パスワードを最後に変更した日時 < 2009/06/30 00:00:00 のユーザを列挙します。
#ファイルとして実行する場合は、実行前にSet-ExecutionPolicy RemoteSignedが必要です
# パスワードは無期限
$ADS_UF_DONT_EXPIRE_PASSWD = 0x10000
#基準日
$date = [datetime] "2009/06/30"

$ou = [ADSI] "LDAP://crm1.local/OU=TestUsers,DC=crm1,DC=local"
$ds = New-Object System.DirectoryServices.DirectorySearcher -argumentList $ou
$ds.Filter = [string]::Format("(&(objectClass=user)(pwdLastSet<={0}))", $date.ToFileTime())
$ds.PropertiesToLoad.Add("sAMAccountName")
$ds.PropertiesToLoad.Add("pwdLastSet")
$ds.PropertiesToLoad.Add("userAccountControl")
# 最大5000件
$ds.SizeLimit = 5000
$ds.PageSize = 1000

foreach($find in $ds.FindAll()){
  $accountname = $find.Properties["samaccountname"][0]
  $msg = [string]::Format("{0}を次回パスワード変更が必要にセット", $accountname )
  Write-Output $msg

  $tick = $find.Properties["pwdlastset"][0]
  $uac = $find.Properties["useraccountcontrol"][0]

  if($uac -band $ADS_UF_DONT_EXPIRE_PASSWD){
    write-output "無期限に設定されています。次回パスワード変更は設定しません。"
    continue
  }
  if($tick -gt 0){
    $pwdLastSet = [System.DateTime]::FromFileTime($tick)
    $msg = [string]::Format("パスワード設定日時{0}", $pwdLastSet)
    write-output $msg

    write-output "パスワードが基準日以降に変更されていません"
    write-output "次回ログイン時にパスワード変更必要を設定します。"
    $de = $find.GetDirectoryEntry()
    $de.pwdLastSet = 0
    #実際にパスワード変更が必要な場合に下記コメントを解除
    #$de.SetInfo()
  }else{
    write-output "パスワードがすでに次回ログイン時にパスワード変更が必要に設定されています"
  }
}

最大5000件の検索条件に該当するユーザを検索し、次回パスワードの変更が必要にセットしています。実際にDirectoryEntryのプロパティへの変更を確定するには、コメントアウトされている#$de.SetInfo()のコメントを解除してください。

サンプルから、いくつかPowerShell で使用するときの注意点がわかります。

項目 説明
論理演算子 -eq, -gt , -lt 等を使用する
bit 演算子 -band 等を使用する
[adsi],[string]などの標準変換が用意されていないインスタンスの作成 New-Object を使用する
スタティック(静的)なメソッド、プロパティへのアクセス [string]::Fomrmat など、[]でクラス、static なメソッドを::で接続して呼び出します。

別の記事でより詳細な演算子の説明、便利なコマンドレットの説明などを行いたいと思います。

2. Active Directory ユーザとコンピュータを使用する場合

スナップインを使用して、同じようのLDAPの検索クエリを設定して条件に該当するユーザを検索できます。その方法を紹介します。

管理ツールメニューから、Active Directory ユーザとコンピュータスナップインを起動します。
メニューの表示→フィルタ オプションを表示します。フィルタオプション画面でカスタムフィルタの作成を選択肢、カスタマイズボタンをクリックします。

カスタムの検索条件ダイアログで詳細設定タブをクリックし、下図のようにLDAP検索クエリをセットします。pwdLastSet の比較で指定している値は2008年6月1日 11時11分11秒のファイル時間です。

ファイル時間の求め方は、PowerShell を起動し、次のようにファイルタイムを取得したい日時を$date に設定して、$date.ToFileTime()を呼び出して取得できます。

PS C:\Documents and Settings\user> $date = [datetime] "2008/6/1 11:11:11"
PS C:\Documents and Settings\user> $date.ToFileTime()
128567598710000000

3. まとめ

今回の説明は以上です。不慣れなPowerShell でがんばって作ったのですが、Active Directory ユーザとコンピュータスナップインのフィルタを使用すれば、さらに簡単に検索を実現できることを教えてもらったので、その方法も同時に掲載しました。

間違い、指摘点などありましたらご連絡ください。