ActiveDirectoryにPowerShellでユーザを一括登録

ユーザアカウント情報を記載したCSVファイルを準備し、これを**Active Directory**に一括登録するスクリプトを作成する。
**dsadd**コマンドによるものが有名だが、ここでは**Power Shell**で作成してみよう。

# 仕様

- １行目をヘッダーとした次のようなCSVファイルを入力可能で、任意に列を追加できるものとする。
![z.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/16414a4e-91e2-ce76-6f0a-8af80e0b558c.png)
- 姓・名・表示名は、CSVファイルのスタッフコード、スタッフ名から設定する。
- 所属するグループを<kbd>:</kbd>区切りで複数指定できる。
- ユーザごとに個別の初期パスワードを指定できる。
- ログオン可能時間を指定した場合、そのまま`net user /times`に渡す。
- アカウントの有効期限を`yyyy/MM/dd`形式で指定できる。

# スクリプト

CSVファイル名を`users.csv`とし、スクリプトと同じフォルダに置く。

```powershell:一括登録スクリプト
ipcsv users.csv -Encoding Default | %{
  $staffCode = $_.'スタッフコード'
  # ユーザ定義用の連想配列を作成
  $users = @{
    SamAccountName = $staffCode  # ログオン名
    employeeID = $staffCode  # 従業員ID
    employeeNumber = $staffCode  # 従業員番号
    Name = $_.'スタッフ名'
    Surname = $staffCode  # 姓
    GivenName = $_.'スタッフ名'  # 名
    DisplayName = $staffCode + ' ' + $_.'スタッフ名'  # 表示名
    Path = 'OU=XXXXX,OU=Users,OU=XXX,DC=xxx,DC=ne,DC=jp'  # OUは固定
    Description = $_.'AD説明'  # 説明欄
    UserPrincipalName = $staffCode + '@xxx.xxx.ne.jp'  # ユーザプリンシパル名
    AccountPassword = (ConvertTo-SecureString -AsPlainText $_.'AD初期パスワード' -force)  # 暗号化したパスワード
    ChangePasswordAtLogon = $True  # 次回ログオン時にパスワード変更が必要
    AccountExpirationDate = $_.'AD有効期限'  # アカウントの有効期限
    Enabled = $True  # ユーザを有効にする
  }
  New-ADUser @users  # アカウント追加

  # ログオン可能時間帯
  if($_.'ADログオン可能時間' -ne '') {
    $cmdstr = '/c net user ' + $staffCode + ' /DOMAIN /TIMES:' + $_.'ADログオン可能時間'
    cmd $cmdstr
  }

  # 所属させるグループ
  $groups = $_.'AD所属グループ' -split ':'
  foreach($group in $groups) {
    Add-ADGroupMember -Identity $group -Members $staffCode
  }
}
```

# 実行結果
スクリプト内で定義した**OU**の下を確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/0af36cde-715f-5470-4d43-de16a843ee59.png)

# 改造してみよう
本項では、上述のスクリプトを元に色々と改造を施してみる。
## スタッフ名を姓と名に分けて登録するには
正規表現（１つ以上の空白）で、姓と名に`split`する。空白が無ければ姓に片寄せとなる。

```powershell
$fullNameArray = $Name -split '\s+'

Surname = $fullNameArray[0]    # 姓
GivenName = $fullNameArray[1]  # 名
```

## フリガナを登録するには
フリガナはベンダー固有の拡張属性である。LDAPの標準規約には無い。
拡張属性は**New-ADUser**の`OtherAttributes`パラメタで指定できるが、ここでは**Set-ADUser**で追加する。

```powershell
$fullNameKana = ($_.'スタッフ名カナ').trim()
$fullNameKanaArray = $fullNameKana -split '\s+'

## フリガナの追加
Set-ADUser -Identity $staffCode -Add @{
  'msDS-PhoneticLastName' = $fullNameKanaArray[0]
  'msDS-PhoneticFirstName' = $fullNameKanaArray[1]
}
```

## 有効期限を実行日基準にするには
アカウント有効期限を作成日の90日後とするなら、次のように修正する。

```powershell
AccountExpirationDate = (Get-Date).AddDays(90).ToString("yyyy/MM/dd")
```

なお、**Active Directory ユーザーとコンピューター** の画面から「アカウントの期限」を確認すると、設定した日付と１日ずれているのは正しい。

例えば、左の画像で、設定した有効期限（`AccountExpires`）は時刻込みの`2019/12/27 00:00:00(JST)`であるが、右の画像では前日の`2019/12/26`で表示される。これは「アカウントの期限」では時刻情報が省略され、その日付が終日有効かどうかで考えるからだ。GMTだから１日ずれると解説しているサイトがあるが、それは誤りである。
![ad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/c4bf13bb-fb48-ea38-ac24-6d58c9bf2b29.png)
※ 属性エディタを表示するには、[表示] メニューから [拡張機能表示] を有効にする。


## 既存アカウントをロックするには
項目「ロック」のフィールドに何か入力されているとき、そのアカウントをロック（無効化）するには、次のようなコードを追加する。
**Get-ADUser** でアカウントが存在するかチェックし、存在したら**Set-ADUser**で`Enabled`を`false`にリセットする。

```powershell
if ($_.'ロック' -ne '') {
  $users = Get-ADUser -Filter {SamAccountName -eq $staffCode}
  if ($users -eq $null) {
    Write-Host "存在しないのでロックできません :$staffCode"
    return  # 次のエントリへ
  }
  Set-ADUser -Identity $staffCode -Enabled $false
  Write-Host "ロックしました :$staffCode"
  return  # 次のエントリへ
}
```

`ForEach-Object (%)`の場合、`continue`ではループの先頭に移動できないので`return`を使う。

## すべてのグループから外れるには
所属させるグループを付け替える場合など、いったん、すべてのグループから外れるには、**Get-ADGroup**と**Remove-ADGroupMember**を組み合わせて使う。

```powershell
$user = Get-ADUser $staffCode -Properties memberOf
$groups = $user.memberOf | %{Get-ADGroup $_}
$groups | %{Remove-ADGroupMember -Identity $_ -Members $user -Confirm:$false}
```

# 補足
ログオン可能時間は`logonHours`オプションでも指定できるが、**New-ADUser**コマンドレットだけで処理しようとするとbyte型の配列（1時間1ビットで24x7=168ビット=21byte）で与えなければならず、お勧めできない。
`net user /times`なら、例えば平日9時〜22時までログインを認める場合に`M-F,09:00-22:00`のように書ける。（カンマが含まれるのでダブルクォーテーションで囲むこと）

テンポラリースタッフ（派遣やバイト）を大量に雇うことを繰り返している会社では有用だろう。