Active Directory の拡張属性 x500uniqueIdentifier を PowerShell で取得すると、バイト列が「10進数の並び」に見えたり、Export-Csv で型名(ADPropertyValueCollection)だけが出力されたりして困ることがあります。この記事では、属性の生データを16進数文字列へ変換してからCSVに出力し、Excelで安全に扱うための実装と注意点を整理します。
なぜ「型名だけ」がCSVに出てしまうのか
Export-Csv は、出力対象のプロパティを「文字列化」してCSVのセルに書き込みます。しかし x500uniqueIdentifier のような拡張属性は、PowerShell上で コレクション型(多くのケースで Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)として返ってきます。
このとき、コレクションの中身(バイト列)をCSVに展開するのではなく、コレクションオブジェクト自体を1つの値として文字列化しようとしてしまい、結果として「型名(クラス名)」が出力されます。
| 現象 | PowerShellで見えるもの | 原因 | 必要な対策 |
|---|---|---|---|
| 画面表示では10進数が並ぶ | 例:4 32 130 10 ... | byte[] が既定表示で「各バイトを10進数」で列挙するため | 16進数へ変換してから扱う |
| CSVに型名だけが出る | Microsoft.ActiveDirectory.Management.ADPropertyValueCollection | Export-Csv がコレクションを中身展開せずに文字列化するため | Select-Object の計算列で「文字列プロパティ」を作る |
x500uniqueIdentifier の性質を押さえる
x500uniqueIdentifier は運用環境によって使われ方が異なりますが、実務上のポイントは次の2点です。
- バイナリ(バイト列)として扱うのが現実的(表示上は10進数の集合に見えることがある)
- 複数値属性になり得る(1ユーザーに複数の値が入るケースがある)
| 項目 | 内容 | 実務での影響 |
|---|---|---|
| データの実体 | バイト列として扱えることが多い | CSVに出すなら16進数文字列へ変換するのが最も扱いやすい |
| PowerShell上の見え方 | 10進数の並び、またはコレクション型 | そのまま出しても人間に読みづらい/CSVで崩れる |
| 複数値属性 | 1ユーザーに複数値が入る可能性 | 「1セルにまとめる」か「値ごとに行を増やす」など設計が必要 |
解決の基本方針:Export-Csv の前に「16進数文字列」に整形する
結論から言うと、やるべきことはシンプルです。
Get-ADUserで x500uniqueIdentifier を取得するSelect-Objectの 計算列で、バイト列を16進数文字列に変換して新しいプロパティを作る- 作った「文字列プロパティ」を
Export-Csvで出力する
ポイントは、Export-Csv に渡す時点で「すでに文字列になっている」状態を作ることです。これで、型名だけが出る問題を確実に回避できます。
採用された方法:1バイトずつ16進数に変換して結合する
今回の課題に対して採用されたのが、[System.Convert]::ToString(値, 16) を使い、コレクション(または列挙されるバイト)を1要素ずつ16進数へ変換してから結合する方法です。
複数ユーザーをCSVへ出力するサンプル
# AD からユーザーを取得し、16進数に変換した x500uniqueIdentifier を付けて出力する
$users = Get-ADUser -Filter * -Properties x500uniqueIdentifier |
Select-Object `
sAMAccountName,
@{ Name = 'Hex500uniqueIdentifier'; Expression = {
# x500uniqueIdentifier は「複数値」なので 1 要素ずつ 16進数に変換
$_.x500uniqueIdentifier | ForEach-Object {
[System.Convert]::ToString($_, 16)
} -join ' ' # 必要に応じて区切り文字を変更(例: '-' など)
}}
# CSV に書き出し(Excel で開ける形式)
$users | Export-Csv 'C:\temp\x500uniqueIdentifier.csv' -NoTypeInformation -Encoding UTF8
この実装の良い点は、PowerShell標準機能だけで完結し、運用上の「とにかく16進数として保存しておきたい」というニーズに素直に応えられるところです。
1ユーザーだけ試す場合
まずは動作確認したい、というケースでは -Filter * を -Identity に差し替えるだけでOKです。
$u = Get-ADUser -Identity "user01" -Properties x500uniqueIdentifier |
Select-Object sAMAccountName,
@{Name='Hex500uniqueIdentifier';Expression={
$_.x500uniqueIdentifier | ForEach-Object { [System.Convert]::ToString($_, 16) } -join ' '
}}
$u | Format-List *
より実務向けにするなら:16進数は「2桁固定」にする
[System.Convert]::ToString($_, 16) は便利ですが、先頭の0が省略されることがあります。たとえば 10進数の 10(16進数で 0A)が a のように出てしまうイメージです。
ログ保存や照合(ハッシュ化、比較、検索)を考えると、16進数は1バイト=2桁で揃えておく方が安全です。そこで次のどちらかに寄せるのがおすすめです。
| 方法 | 例 | メリット | 注意点 |
|---|---|---|---|
書式演算子 -f | "{0:x2}" -f $_ | 常に2桁になり、最もシンプル | 出力は小文字(x2)/大文字は X2 |
ToString + PadLeft | [Convert]::ToString($_,16).PadLeft(2,'0') | 元のロジックを保ちつつ調整できる | やや冗長 |
2桁固定にした改良版(おすすめ)
$users = Get-ADUser -Filter * -Properties x500uniqueIdentifier |
Select-Object sAMAccountName,
@{ Name = 'Hex500uniqueIdentifier'; Expression = {
if (-not $_.x500uniqueIdentifier) { return '' }
$_.x500uniqueIdentifier | ForEach-Object {
# 1バイト=2桁に固定(小文字)
"{0:x2}" -f $_
} -join ' '
}}
$users | Export-Csv 'C:\temp\x500uniqueIdentifier.csv' -NoTypeInformation -Encoding UTF8
「Excelで見たい」だけなら元の実装でも十分なことが多いですが、後から照合や突合が発生しやすい属性なので、できれば2桁固定を癖にしておくとトラブルが減ります。
区切り文字はどう選ぶべきか
16進数文字列の出力は、運用目的によって「見やすさ」と「機械処理のしやすさ」が変わります。典型的な選択肢を整理します。
| 区切り | 例 | 向いている用途 | Excelでの扱い |
|---|---|---|---|
| スペース | 04 20 82 0a ... | 人間が目視で確認したい | セル内で見やすいが、分割は追加作業 |
| ハイフン | 04-20-82-0a-... | 一般的なバイト列表記に寄せたい | 検索・コピーがしやすい |
| 区切りなし | 0420820a... | 比較・照合・正規化(同一性チェック) | 短くなるが視認性は下がる |
「Excelで眺めながら、必要なら後で処理する」ならスペース区切りが無難です。一方、「別システムに転記して比較する」「突合ツールで一致判定する」なら区切りなしの方が扱いやすいこともあります。
区切りなしにする例
@{ Name='Hex500uniqueIdentifier'; Expression = {
if (-not $_.x500uniqueIdentifier) { return '' }
($_.x500uniqueIdentifier | ForEach-Object { "{0:X2}" -f $_ }) -join ''
}}
複数値属性への対応パターン
x500uniqueIdentifier は複数値になり得るため、「1ユーザー=1行」のCSVを作るなら、値をどう表現するか決める必要があります。現場でよく採用されるのは次の2パターンです。
| パターン | 出力イメージ | メリット | デメリット |
|---|---|---|---|
| 1セルにまとめる | 値1 | 値2 | 値3 | 行数が増えない/Excelで一覧しやすい | 値単位のフィルタ・集計がしづらい |
| 値ごとに行を増やす | 同じユーザーが複数行 | 値単位で検索・集計しやすい | 行数が増える/ユーザー単位の集計に工夫が必要 |
パターンA:1セルにまとめる(今回の方法)
今回のサンプルはこの方針です。値が複数ある場合でも、区切り文字で連結し、1行で完結させます。
パターンB:値ごとに行を増やす(分析向け)
後工程で「この16進値を持っているユーザーは誰か」を頻繁に調べるなら、値を1行ずつにした方が楽です。
$rows = Get-ADUser -Filter * -Properties x500uniqueIdentifier |
ForEach-Object {
$sam = $_.sAMAccountName
if (-not $_.x500uniqueIdentifier) {
[PSCustomObject]@{
sAMAccountName = $sam
Hex500uniqueIdentifier = ''
}
return
}
foreach ($v in $_.x500uniqueIdentifier) {
# 2桁固定で16進数化(区切りなし)
$hex = ($v | ForEach-Object { "{0:X2}" -f $_ }) -join ''
[PSCustomObject]@{
sAMAccountName = $sam
Hex500uniqueIdentifier = $hex
}
}
}
$rows | Export-Csv 'C:\temp\x500uniqueIdentifier_rows.csv' -NoTypeInformation -Encoding UTF8
この形式だと、Excelでフィルターを掛けるだけで「このHex値を持つユーザー一覧」がすぐ作れます。
Excelで文字化け・列崩れを防ぐCSV出力のコツ
今回の属性値自体は16進数(英数字)なので文字化けは起きにくいのですが、CSVには sAMAccountName 以外の列を追加することも多く、表示名(日本語)などが混ざると途端に問題化します。Excelの取り込みは環境差があるため、次の観点で設計しておくと安全です。
エンコーディング選びの目安
| 状況 | 推奨 | 理由 |
|---|---|---|
| 新しいExcelでUTF-8が普通に開ける | -Encoding UTF8 | 最も標準的。多言語にも強い |
| 古いExcel/環境依存でUTF-8が崩れる | (環境に合わせて)-Encoding Default 等も検討 | CP932(Shift_JIS系)で開きやすいケースがある |
| PowerShell 7系で「BOM付きUTF-8」を明示したい | -Encoding UTF8BOM | Excel側がBOMで判定しやすい |
「まずは確実にExcelで見たい」が最優先なら、社内の標準(Excelバージョンや言語設定)に合わせてエンコーディングを固定し、手順書化しておくのがおすすめです。
CSVの区切りとExcelの取り込み
CSVの区切りはカンマが基本ですが、Excelの地域設定によってはセミコロン区切りが期待されることもあります。もし「列が1列にまとまってしまう」場合は、Excelの「データ」タブから「テキスト/CSVから」を使ってインポートし、区切り記号と文字コードを明示すると安定します。
出力が正しいかを簡単に検証する方法
16進数化した値は、後から「元のバイト列に戻せる」ことが重要です。運用での監査や突合に備えて、簡易チェックの方法も用意しておくと安心です。
スペース区切りの16進数をバイト配列へ戻す(検証用)
# 例: "04 20 82 0a" のような文字列を byte[] に戻す
$hex = "04 20 82 0a"
$bytes = $hex.Split(' ') | ForEach-Object { [Convert]::ToByte($_, 16) }
# 先頭数バイトを確認
$bytes | Select-Object -First 16
このように「戻せる」ことを確認できれば、CSVに保存した値が単なる飾りではなく、再利用可能なデータとして扱えます。
もう少し踏み込んだ実務テクニック
対象ユーザーを絞って負荷と時間を減らす
-Filter * は全件取得なので、ユーザー数が多い環境では負荷がかかります。OU単位で対象を限定する、条件を付けるなどして、目的に合う範囲だけを抽出すると安定します。
# 例:特定OU配下だけに限定
$base = "OU=Users,DC=example,DC=com"
Get-ADUser -SearchBase $base -Filter * -Properties x500uniqueIdentifier |
Select-Object sAMAccountName,
@{Name='Hex500uniqueIdentifier';Expression={
if (-not $*.x500uniqueIdentifier) { return '' }
($*.x500uniqueIdentifier | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
}} |
Export-Csv 'C:\temp\x500uniqueIdentifier_ou.csv' -NoTypeInformation -Encoding UTF8
「区切り」「大文字/小文字」「2桁固定」をルール化する
運用が長くなると、同じ属性を別の担当者が別形式(スペース区切り、小文字、桁数不揃い)で出力してしまい、後で突合できなくなる事故が起きがちです。
- 表記:大文字(
X2)か小文字(x2)か - 1バイト:2桁固定にするか
- 区切り:スペース/ハイフン/区切りなし
この3点は、チーム内で一度決めたらスクリプトに固定し、毎回同じ形式で出力するのがベストです。
よくあるつまずき(FAQ)
Q. PowerShellで見える「10進数の並び」は何?壊れている?
A. 壊れているわけではありません。byte[](バイト配列)が既定の表示で「各バイトを10進数で列挙」しているだけです。値の実体はバイト列なので、16進数に変換すれば人間にも扱いやすくなります。
Q. なぜ Export-Csv で中身ではなく型名だけが出るの?
A. x500uniqueIdentifier がコレクション型として返るためです。Export-Csv はオブジェクトをそのまま文字列化するので、コレクションの中身ではなく、コレクション型の名前が出力されます。対策は、Select-Object の計算列で文字列化したプロパティを作ることです。
Q. 16進数に変換したら、先頭が「0」になるはずの桁が消える
A. [Convert]::ToString(値,16) は先頭の0を省略します。1バイト=2桁に揃えたい場合は "{0:X2}" -f $_ のような書式指定を使ってください。
Q. 属性の中身はBER/ASN.1みたいな構造になっていると聞いた
A. 先頭に種類や長さが付くなど、エンコード構造を持つケースがあるのは事実として知られています。ただし多くの現場では、構造解析よりも「まず生データを失わずに保存し、後で必要になったときに解析できる状態にする」方が重要です。今回の方法は、バイト列をそのまま16進数で保存するため、後工程の解析にも繋げやすいのが利点です。
まとめ:ADの拡張属性をCSV/Excelで扱うなら「文字列化」が勝ち筋
- Active Directory の x500uniqueIdentifier は、PowerShell上でコレクション型として返り、
Export-Csvでは型名だけが出力されることがある - 対策は
Select-Objectの計算列で、バイト列を16進数文字列に変換してからCSVへ出力すること - 実務では「1バイト=2桁固定(
X2/x2)」にしておくと、照合や検索が強くなる - 複数値属性である点を踏まえ、「1セルにまとめる」か「値ごとに行を増やす」かを用途で選ぶ
この方針で作ったCSVなら、Excelでの目視確認・フィルタリングはもちろん、後で突合や再解析が必要になったときにも、元のバイト列へ戻せる形でデータを保持できます。

コメント