1. 記事一覧 >
  2. ブログ記事
Windows
category logo

AD(Active Directory)の中身を覗いてみた - ldapsearch,php,PowerShell

(更新) (公開)

はじめに

WindowsのAD(Active Directory)は、LDAPのように、ツリー構造のデータベースで管理されています。そのため、OpenLDAPのldapsearchコマンドで中身を見られます。
ldapsearch以外にもいろいろな方法で中身を見てみました。

Active Directoryとは?LDAPとは?ldapsearchとは?の説明は割愛します。

【検証環境】

Windows Server 2019 Datacenter Desktop

 ドメインコントローラーに昇格したActive Directory

FreeBSD13(本ブログ別記事「FreeBSD-13.0-RELEASEにapache2,php,postgresql,openldapをインストール」まで実施したサーバーです。)

 OpenLDAP: ldapsearch 2.4.59

 PHP 7.4.23

以下、全て、 ユーザー:dn: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com を見ていきます。画面で入力できるところは、適当にできるだけ埋めました。

ユーザー:dn: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com

【2023年12月10日追記】

ldapsearch、LDPなどの他に Active Directory ユーザーとコンピューター属性エディター タブでも見られることに関しては、割愛します。


手順:

1.表示→拡張機能をON

表示→拡張機能をON


2.ユーザーのプロパティを開く→属性エディタータブを開く

属性エディタータブ


ldapsearchで見る

ADがあるWindows Server 2019とは別のFreeBSD13のサーバーからldapsearchコマンドで見てみます。


dc=ad,dc=contoso,dc=com部分は、ドメイン名によります。今回は、ドメインがad.contoso.comのため、こうなります。
cn=administrator,cn=users,dc=ad,dc=contoso,dc=comは、ADサーバーのAdministratorのことです。
passwordは、ADがあるWindows Server 2019のAdministratorのパスワード
192.168.12.219は、ADがあるWindows Server 2019のIPアドレスです。

# ldapsearch -x -D "cn=administrator,cn=users,dc=ad,dc=contoso,dc=com" -w "password" -h "192.168.12.219" -b "CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com" -s base
# extended LDIF
#
# LDAPv3
# base <CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com> with scope baseObject
# filter: (objectclass=*)
# requesting: ALL
#

# \E5\B1\B1\E7\94\B0 \E5\A4\AA\E9\83\8E, Users, ad.contoso.com
dn:: Q0495bGx55SwIOWkqumDjixDTj1Vc2VycyxEQz1hZCxEQz1jb250b3NvLERDPWNvbQ==
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn:: 5bGx55SwIOWkqumDjg==
sn:: 5bGx55Sw
c: JP
l:: 6LGK55Sw5biC4peL4peL55S6
st:: 5oSb55+l55yM
title:: 6YOo6ZW3
description:: 5bGx55SwIOWkqumDjuOBruiqrOaYjg==
postalCode: 000-0000
postOfficeBox:: 56eB5pu4566xWA==
physicalDeliveryOfficeName:: 5bGx55SwIOWkqumDjuOBruS6i+alreaJgA==
telephoneNumber: 090-0000-0000
givenName:: 5aSq6YOO
・・・(略)・・・
objectSid:: AQUAAAAAAAUVAAAAJGuaEgRxwvsG8AHMTwQAAA==
adminCount: 1
accountExpires: 132854364000000000
logonCount: 0
sAMAccountName: yamada.taro
sAMAccountType: 805306368
otherFacsimileTelephoneNumber: 000-000-0001
otherMobile: 090-0000-0000
managedObjects:: Q04944Kw44Or44O844OXWCxDTj1Vc2VycyxEQz1hZCxEQz1jb250b3NvLERDP
 WNvbQ==
userPrincipalName: yamada.taro@ad.contoso.com
otherIpPhone: 000-000-00002
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=contoso,DC=com
dSCorePropagationData: 20210923090647.0Z
dSCorePropagationData: 16010101000000.0Z
msDS-PhoneticFirstName:: 44K/44Ot44Km
msDS-PhoneticLastName:: 44Ok44Oe44OA
msDS-PhoneticDepartment:: 44Kx44Kk44Ko44Kk44Kt44Kr44Kv44OW
msDS-PhoneticCompanyName:: 44Kr44OW44K344Kt44Ks44Kk44K344Oj44Oe44Or44Oe44Or
msDS-PhoneticDisplayName:: 44Ok44Oe44OAIOOCv+ODreOCpg==
mail: yamada.taro@contoso.com
manager:: Q04955Sw5LitIOS4iuWPuCxDTj1Vc2VycyxEQz1hZCxEQz1jb250b3NvLERDPWNvbQ==

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

2バイト文字が有るところは、base64になっています。
また、objectSidその他、バイナリデータが格納されているところもbase64になっています。ちなみに、当たり前ですが、バイナリデータをbase64デコードして表示すると文字化けするだけで、読めません。


バイナリデータが文字化けしても構わない場合、以下のようにbase64部分をデコードできます。

# ldapsearch -x -D "cn=administrator,cn=users,dc=ad,dc=contoso,dc=com" -w "password" -h "192.168.12.219" -b "CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com" -s base | perl -MMIME::Base64 -MEncode=decode -n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode("UTF-8",decode_base64($1))/eg;binmode(STDOUT, ":utf8");print'
# extended LDIF
#
# LDAPv3
# base <CN=文字化け,CN=Users,DC=ad,DC=contoso,DC=com> with scope baseObject
# filter: (objectclass=*)
# requesting: ALL
#

# \E5\B1\B1\E7\94\B0 \E5\A4\AA\E9\83\8E, Users, ad.contoso.com
dn:: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn:: 山田 太郎
sn:: 山田
c: JP
l:: 豊田市○○町
st:: 愛知県
title:: 部長
description:: 山田 太郎の説明
postalCode: 000-0000
postOfficeBox:: 私書箱X
physicalDeliveryOfficeName:: 山田 太郎の事業所
telephoneNumber: 090-0000-0000
givenName:: 太郎
・・・(略)・・・
objectSid:: 文字化け
adminCount: 1
accountExpires: 132854364000000000
logonCount: 0
sAMAccountName: yamada.taro
sAMAccountType: 805306368
otherFacsimileTelephoneNumber: 000-000-0001
otherMobile: 090-0000-0000
managedObjects:: CN=グループX,CN=Users,DC=ad,DC=contoso,DC=com
userPrincipalName: yamada.taro@ad.contoso.com
otherIpPhone: 000-000-00002
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=contoso,DC=com
dSCorePropagationData: 20210923090647.0Z
dSCorePropagationData: 16010101000000.0Z
msDS-PhoneticFirstName:: タロウ
msDS-PhoneticLastName:: ヤマダ
msDS-PhoneticDepartment:: ケイエイキカクブ
msDS-PhoneticCompanyName:: カブシキガイシャマルマル
msDS-PhoneticDisplayName:: ヤマダ タロウ
mail: yamada.taro@contoso.com
manager:: CN=田中 上司,CN=Users,DC=ad,DC=contoso,DC=com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

phpで見る

ldap_search関数等、ldap_*関数で検索するプログラムを作りました。
※dc=ad,dc=contoso,dc=com以下全てのエントリが表示されます。


なお、objectSid、objectGUIDは、バイナリデータですが、特殊な仕様のため、仕様に合わせた文字列化処理が必要でした。
objectSidは、https://stackoverflow.com/questions/39533560/php-ldap-get-user-sidfunction bin_to_str_sid($binary_sid) {
objectGUIDは、https://www.null-byte.org/development/php-active-directory-ldap-authentication/public static function GUIDtoString($ADguid)を実装して、正しく動作しました。(あらゆるケースで正しいかは不明です。)
また、userParametersもバイナリデータですが、かなり特殊な仕様になっていて、なんとか確認できましたが、長くなるため、以下のphpは、bin2hexしているだけです。詳細は、下記「userParametersについて」を参照してください。
その他のバイナリデータについてもbin2hexしているだけになります。


ad.contoso.comは、ADがあるWindows Server 2019のサーバー名です。(名前解決が必要です。)
passwordは、ADがあるWindows Server 2019のAdministratorのパスワード
$ldaprdn = 'ad' . "\\" . $username;は、ドメイン名\ユーザー名です。このままの場合、ad\Administratorになります。
dc=ad,dc=contoso,dc=com部分は、ドメイン名によります。今回は、ドメインがad.contoso.comのため、こうなります。

<?php
// ldapスキームで接続するLDAPサーバーのアドレスを設定します。
$adServer = "ldap://ad.contoso.com";

// LDAPサーバーに接続します。この時点ではまだ接続は認証されていません。
$ldap = ldap_connect($adServer);

// 認証に使用するユーザー名を設定します。
$username = "Administrator";

// ユーザーのパスワードを設定します。
$password = "password";

// LDAPバインドの際のユーザーの認証情報「ドメイン名\ユーザー名」をldaprdnに設定。
$ldaprdn = 'ad' . "\\" . $username;

// LDAPプロトコルのバージョンを3に設定します。LDAPv3はよりセキュアな認証メカニズムを提供します。
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);

// LDAPのReferralsオプションを無効にします。有効にすると、LDAPクライアントが求めた情報をサーバーが持っていない場合、別のサーバーに参照されます。
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

// LDAPサーバーに対して指定されたユーザー名とパスワードを使用してバインドを行います。
$bind = ldap_bind($ldap, $ldaprdn, $password);

// バインドが成功した場合のみ、次の処理に進みます。
if ($bind) {
    // LDAP検索フィルターを設定します。ここでは全てのオブジェクトクラスを対象にしています。
    $filter = "objectclass=*";

    // 指定したベースDNでLDAP検索を実行します。ここではad.contoso.comを検索対象としています。
    $rt = ldap_search($ldap, "dc=ad,dc=contoso,dc=com", $filter);

    // 結果を格納するための空の配列を作成します。
    $results = array();

    // 検索結果の最初のエントリ(レコード)を取得します。
    $entry_id = @ldap_first_entry($ldap, $rt);

    // 各エントリに対して処理を繰り返します。
    while ($entry_id != false) {
        // 各エントリの結果を格納するための配列を初期化します。
        $result = array();

        // 現在のエントリの属性とその値を取得します。
        $vals = @ldap_get_attributes($ldap, $entry_id);

        // count キーで属性の総数を取得します。
        $attrnum = intval($vals["count"]);

        // 各属性について処理を繰り返します。
        for ($n = 0; $n < $attrnum; $n ++) {
            // 属性名を取得します。
            $attr = $vals[$n];

            // 属性名を小文字に変換します。
            $key = strtolower($attr);

            // その属性の値の総数を取得します。
            $num = intval($vals[$attr]["count"]);

            // 属性に値がある場合、それをresult配列に繰り返し追加します。
            if ($num != 0) {
                $result[$key] = array();
                for ($no = 0; $no < $num; $no ++) {
                    // 属性の値を取得し、配列に追加します。
                    $value = $vals[$attr][$no];
                    $result[$key][$no] = $value;
                }
                // 値が1つだけの場合、配列形式ではなく単一の値として$result[$key]に再代入します。
                if (isset($result[$key]) && count($result[$key]) == 1) {
                    $result[$key] = $result[$key][0];
                }
            }
        }

        // 現在処理中のエントリのDN(Distinguished Name)を取得し、result配列に追加します。
        $result["0dn"] = @ldap_get_dn($ldap, $entry_id);

        // バイナリデータを文字列に変換する関数を呼び出します。
        Bin2String($result);

        // 現在処理中のresult配列をresults配列に追加します。
        $results[] = $result;

        // 次のエントリを取得します。
        $entry_id = @ldap_next_entry($ldap, $entry_id);
    }

    // LDAP接続を閉じます。
    @ldap_close($ldap);

    // すべてのLDAP検索結果をループ処理します。
    foreach ($results as $result) {
        // 結果配列をキーでソートします。これにより、属性が一定の順序で表示されます。
        ksort($result);

        // 各エントリの各属性についてループ処理を行います。
        foreach ($result as $key => $value) {
            // '0dn'キーを見つけたら、キー名を'dn'に変更して表示します。それ以外はそのまま代入します
            $attr = strcmp($key, "0dn") !== 0 ? $key : "dn";

            // 属性の値が配列(複数の値を持つ)の場合、それらをセミコロンで区切って一つの文字列に結合します。
            // 例えば、複数のメールアドレスや電話番号など。
            if (is_array($value)) {
                echo $attr.": ".implode("; ", $value).";\n";
            } else {
                // 属性の値が単一の場合、そのまま値を表示します。
                echo $attr.": ".$value.";\n";
            }
        }
        // 各エントリの終わりに改行を入れて、エントリ間を区切ります。
        echo "\n";
    }
    // LDAPバインド(接続と認証のプロセス)が失敗した場合
} else {
    // エラーメッセージを表示してユーザーに通知します。
    $msg = "Invalid bind dn / password";
    echo $msg;
}

// 以下、バイナリデータを文字列に変換するための補助関数群です。これらは特定のバイナリ属性を適切な文字列形式に変換します。

function Bin2String(&$result)
{
    // どの変換関数をどの属性に適用するかを定義します。
    $conv_func_attrs["SIDtoString"] = array("objectsid");
    $conv_func_attrs["GUIDtoString"] = array("objectguid", "msdfsr-replicationgroupguid", "msdfsr-contentsetguid");
    $conv_func_attrs["bin2hex"] = array("auditingpolicy", "dsasignature", "dnsrecord", "ipsecdata", "samdomainupdates", "msds-generationid", "userparameters");

    // すべての変換関数と属性についてループ処理を行います。
    foreach ($conv_func_attrs as $func => $attrs) {
        // 各属性に対してループ処理を行います。
        foreach ($attrs as $attr) {
            // 対象の属性がセットされていて、かつ空でない場合のみ処理を行います。
            if (isset($result[$attr]) && !empty($result[$attr])) {
                // 指定された変換関数をその属性の値に適用します。
                // '@'はエラーを抑制するために使われています。
                $result[$attr] = @$func($result[$attr]);
            }
        }
    }   
}

function SIDtoString($value)
{
    // 'unpack' 関数を使用してバイナリデータを解析し、SID(個々のユーザーまたはグループに対して与えられる識別用の番号)の構成要素を取得します。
    // C1rev: 1バイトのデータを無符号文字として読み取り、rev(リビジョン番号、ファイル等のバージョンを一意に識別するもの)として格納します。
    // C1count: 次の1バイトを無符号文字として読み取り、サブオーソリティ(SIDの一部で、ユーザーやグループを一意に識別し、その役割や権限を区別するための情報)の数を示す count として格納します。
    // x2: 2バイトをスキップします。この部分はSIDのフォーマットにおいて特に意味を持たないため読み飛ばされます。
    // N1id: 次の4バイトを無符号のロング(32ビット整数)として読み取り、これを id(識別子権限)として格納します。
    $sid = @unpack('C1rev/C1count/x2/N1id', $value);

    // サブオーソリティの配列を初期化します。
    $subAuthorities = [];

    // SIDの構成要素が正しく取得できたかを確認します。
    if (!isset($sid['id']) || !isset($sid['rev'])) {
        // リビジョンレベルまたは識別子権限が見つからない場合、エラーになります。
        throw new \UnexpectedValueException(
            'The revision level or identifier authority was not found when decoding the SID.'
        );
    }

    // SIDのリビジョンレベルと識別子権限を取得します。
    $revisionLevel = $sid['rev'];
    $identifierAuthority = $sid['id'];

    // サブオーソリティの数を取得します。
    $subs = isset($sid['count']) ? $sid['count'] : 0;

    // 各サブオーソリティを解析し、配列に追加します。
    for ($i = 0; $i < $subs; $i++) {
        // Vはバイナリデータを「リトルエンディアン」フォーマットとして解析、
        // 1は解析する要素の数、
        // subは解析されたデータを格納するための連想配列のキー名
        $subAuthorities[] = unpack('V1sub', hex2bin(substr(bin2hex($value), 16 + ($i * 8), 8)))['sub'];
    }

    // 解析されたSIDの構成要素を組み合わせて、読みやすい形式の文字列を作成します。
    return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode(
        preg_filter('/^/', '-', $subAuthorities)
    );
}

function GUIDtoString($ADguid)
{
    // Active Directory GUIDを16進数の文字列に変換し、2文字ごとに分割して配列に格納します。
    $guidinhex = str_split(bin2hex($ADguid), 2);

    // GUIDを構築するための空の文字列を初期化します。
    $guid = "";

    // 最初の8文字(4バイト)は逆順で取得します。
    $first = array_reverse(array_slice($guidinhex, 0, 4));
    // 逆順にしたバイトを文字列に追加します。
    foreach ($first as $value) {
        $guid .= $value;
    }
    // ハイフンを追加します。
    $guid .= "-";

    // 次の4文字(2バイト)を逆順で取得します。
    $second = array_reverse(array_slice($guidinhex, 4, 2, true), true);
    // これらを文字列に追加します。
    foreach ($second as $value) {
        $guid .= $value;
    }
    // ハイフンを追加します。
    $guid .= "-";

    // 次の4文字(2バイト)を逆順で取得します。
    $third = array_reverse(array_slice($guidinhex, 6, 2, true), true);
    // これらを文字列に追加します。
    foreach ($third as $value) {
        $guid .= $value;
    }
    // ハイフンを追加します。
    $guid .= "-";

    // 次の4文字(2バイト)を取得します。
    $fourth = array_slice($guidinhex, 8, 2, true);
    // これらを文字列に追加します。
    foreach ($fourth as $value) {
        $guid .= $value;
    }
    // ハイフンを追加します。
    $guid .= "-";

    // 最後の12文字(6バイト)を取得します。
    $last = array_slice($guidinhex, 10, 16, true);
    // これらを文字列に追加します。
    foreach ($last as $value) {
        $guid .= $value;
    }

    // 構築したGUID文字列を返します。
    return $guid;
}
?>

実行してみます。
※実際には、dc=ad,dc=contoso,dc=com以下全てのエントリが表示されますが、抜粋して1エントリだけの結果例です。

# vi ADSearch.php
(上記プログラムを記述して上書き)
# php ADSearch.php
・・・(他のエントリについては略)・・・
dn: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com;
accountexpires: 132854364000000000;
admincount: 1;
badpasswordtime: 0;
badpwdcount: 0;
c: JP;
cn: 山田 太郎;
co: 日本;
codepage: 0;
company: 株式会社丸丸;
countrycode: 392;
department: 経営企画部;
description: 山田 太郎の説明;
directreports: CN=佐藤 部下,CN=Users,DC=ad,DC=contoso,DC=com;
displayname: 山田 太郎;
distinguishedname: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com;
dscorepropagationdata: 20210923090647.0Z; 16010101000000.0Z;
givenname: 太郎;
homedirectory: C:\profile\yamadashome;
info: 電話
メモ;
instancetype: 4;
l: 豊田市○○町;
lastlogoff: 0;
lastlogon: 0;
logoncount: 0;
mail: yamada.taro@contoso.com;
managedobjects: CN=グループX,CN=Users,DC=ad,DC=contoso,DC=com;
manager: CN=田中 上司,CN=Users,DC=ad,DC=contoso,DC=com;
memberof: CN=グループX,CN=Users,DC=ad,DC=contoso,DC=com;
msds-phoneticcompanyname: カブシキガイシャマルマル;
msds-phoneticdepartment: ケイエイキカクブ;
msds-phoneticdisplayname: ヤマダ タロウ;
msds-phoneticfirstname: タロウ;
msds-phoneticlastname: ヤマダ;
name: 山田 太郎;
objectcategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=contoso,DC=com;
objectclass: top; person; organizationalPerson; user;
objectguid: 2226c0e2-03df-4696-a09c-8c8e051b2b66;
objectsid: S-1-5-21-312109860-4223824132-3422679046-1103;
otherfacsimiletelephonenumber: 000-000-0001;
otherhomephone: 000-0000-0000;
otheripphone: 000-000-00002;
othermobile: 090-0000-0000;
otherpager: 000-0000-0000;
physicaldeliveryofficename: 山田 太郎の事業所;
postalcode: 000-0000;
postofficebox: 私書箱X;
primarygroupid: 513;
profilepath: \\192.168.12.219\profile\yamada.taro;
pwdlastset: 132768645397378719;
samaccountname: yamada.taro;
samaccounttype: 805306368;
scriptpath: C:\Windows\SYSVOL\domain\scripts\test.bat;
sn: 山田;
st: 愛知県;
streetaddress: 1-1;
telephonenumber: 090-0000-0000;
title: 部長;
useraccountcontrol: 512;
userparameters: ・・・16進数表記文字列(略)・・・
userprincipalname: yamada.taro@ad.contoso.com;
usnchanged: 16472;
usncreated: 16408;
whenchanged: 20210923095937.0Z;
whencreated: 20210923082520.0Z;
wwwhomepage: https://www.contoso.com/;
・・・(他のエントリについては略)・・・

日本語が表示されています。
objectSid、objectGUIDが文字列で表示されています。
userParametersが特殊な仕様のバイナリデータのため、bin2hexで16進数表記で表示しています。詳細は、下記「userParametersについて」を参照してください。


LDPで見る

Windows Server 2019に最初から入っている LDP というツールを使って、Active Directoryのデータを見ることができます。それを使って見てみます。
Administratorでログインしているとします。


ファイル名を指定して実行のところに ldp を入力して起動します。

ファイル名を指定して実行のところに ldp を入力して起動

「接続」→「接続」をクリックします。

「接続」→「接続」をクリック


サーバー:localhost
ポート:389
とし、「OK」をクリックします。

サーバーポート


「接続」→「バインド」をクリックします。

「接続」→「バインド」をクリック


バインドの種類:現在のログオン ユーザーとしてバインド
バインド後トラフィックを暗号化するにチェック
とし、「OK」をクリックします。

バインドの種類バインド後トラフィックを暗号化するにチェック


「表示」→「ツリー」をクリックします。

「表示」→「ツリー」をクリック


ベースDN:CN=Users,DC=ad,DC=contoso,DC=com
とし、「OK」をクリックします。

ベースDN


あとは、左側ペインのツリーを開いて、見たいエントリをダブルクリックになります。

左側ペインのツリーを開いて、見たいエントリをダブルクリック


エントリをダブルクリック 下側


日本語が表示されています。
objectSid、objectGUIDが文字列で表示されています。
userParametersが<ldp: Binary blob 1370 bytes>となっていて、中身が分かりません。
sAMAccountType: 805306368 = ( NORMAL_USER_ACCOUNT );など、独自の表記になっている部分があります。


LDPでOpenLDAPにも繋いでみましたが、接続、閲覧できました。ただ、詳しく調べていませんが、ハングアップしました。


PowerShellで見る

PowerShellのコマンドレットGet-ADUserで見てみます。
いろいろな検索方法がありますが、
SamAccountNameyamada.taroのプロパティを全て表示するという意味の

Get-ADUser yamada.taro -Properties *

を実行します。

Get-ADUser yamada.taro -Properties *


Get-ADUser yamada.taro -Properties * 下側


日本語が表示されています。
objectSid、objectGUIDが文字列で表示されています。
userParametersは文字化けしていて、中身が分かりません。


userParametersについて

こちら(stackoverflow)class TSPropertyfunction userParameters($userParameters){で抽出してみましたが、どちらも同じ結果で、一部読めるところがありますが、文字化けしました。
※phpのバージョンなどの影響が有るかもしれませんが、これ以上調査していません。


userParametersをphpで解析してprint_rした結果:

userParametersをprint_rした結果


PowerShellで書かれているこちら(gist.github.com)を見つけました。
ConvertFrom-UserParameter.ps1をパスが通るところに置いて実行します。


. ConvertFrom-UserParameter.ps1で読み込んで、function ConvertFrom-UserParameter {を実行可能とし、
(Get-ADUser yamada.taro -Properties "userParameters").userParametersは、Get-ADUser yamada.taro -Properties "userParameters"でuserParametersだけの属性、値のペアを取り出して、.userParametersで値だけ取り出しています。
それをパイプで、function ConvertFrom-UserParameterの引数として渡しています。

PS C:\Users\Administrator> . ConvertFrom-UserParameter.ps1
PS C:\Users\Administrator> (Get-ADUser yamada.taro -Properties "userParameters").userParameters | ConvertFrom-UserParameter -ShowAll
Name                           Value
----                           -----
CtxCfgPresent                  1428032432
CtxCfgFlags1                   {INHERITMAXDISCONNECTIONTIME, DISABLECCM, INHERITAUTOCLIENT, INHERITMAXIDLETIME...}
CtxShadow                      EnableInputNotify
CtxMinEncryptionLevel          1
CtxWorkDirectory               C:\Windows\system32
CtxWorkDirectoryW              C:\Windows\system32
CtxWFHomeDir                   C:\RDSprofile\yamadashome
CtxWFHomeDirW                  C:\RDSprofile\yamadashome
CtxWFProfilePath               \\192.168.12.219\RDSprofile\yamada.taro
CtxWFProfilePathW              \\192.168.12.219\RDSprofile\yamada.taro
CtxInitialProgram              C:\Windows\system32\notepad.exe
CtxInitialProgramW             C:\Windows\system32\notepad.exe

表示されました!

loading...