【Office 365】階層型アドレス帳の階層構造をリストに出力するPowerShellスクリプトを作成した

# はじめに

Office 365(Exchange Online)の階層型アドレス帳の階層構造をリストに出力するPowerShellスクリプトを作成したのでメモ。
**加工しやすさ重視**のリストを出力するスクリプトと**パッと見の分かりやすさ重視**のリストを出力するスクリプトの２パターンで作成してみた。

手順作成は以下のような階層型アドレス帳に対して行った。（作成環境のライセンスが足りなかったのでユーザーの代わりに連絡先を使っている）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258416/cfb4b2ce-b81c-5663-8d28-732570f149fc.png)

# スクリプト

## 加工しやすさ重視のスクリプト

スクリプトは以下の通り。

```GetGalStructure.ps1
#getGroupTree関数定義
function getGroupTree($targetRecipient, $upperHierarchy){

    #$Datasに現在の$targetRecipientの情報を追記
    $Data = New-Object PSObject | Select-Object upperHierarchy, DisplayName, PrimarySmtpAddress, Title, RecipientType
    $Data.upperHierarchy = $upperHierarchy -join " - "
    $Data.DisplayName = $targetRecipient.DisplayName
    $Data.PrimarySmtpAddress = $targetRecipient.PrimarySmtpAddress
    $Data.Title = $targetRecipient.Title
    $Data.RecipientType = $targetRecipient.RecipientType
    [void]$Datas.Add($Data)

    #$targetRecipientがグループの場合
    if($targetRecipient.RecipientType -eq "MailUniversalSecurityGroup"){

        #$upperHierarchyに$targetRecipientを追加       
        $upperHierarchy += @($targetRecipient.DisplayName)

        #$targetRecipientのメンバー取得
        Get-DistributionGroupMember $targetRecipient.Alias |
            #取得したメンバーをユーザー、連絡先、グループの順で並び替え
            Select-Object Alias, DisplayName, PrimarySmtpAddress, Title, RecipientType, @{L="SortIndex"; E={switch($_.RecipientType){
                "UserMailBox"{0}
                "MailContact"{1}
                "MailUniversalSecurityGroup"{2}
            }}} |
            Sort-Object -Property SortIndex, DisplayName |
            #取得したメンバーに対してgetGroupTree関数を再帰実行
            ForEach-Object {getGroupTree $_ $upperHierarchy}

    }
}

#取得したGAL構造を格納するオブジェクト$Datasを定義
$global:Datas = New-Object System.Collections.ArrayList

#GALルート取得、チェック
$treeRootName = (Get-OrganizationConfig).HierarchicalAddressBookRoot
if([string]::IsNullOrEmpty($treeRootName)){"HierarchicalAddressBookRoot未指定。スクリプトの実行を中断します。";exit}

#GALルートに対してgetGroupTree関数を実行
getGroupTree (Get-DistributionGroup $treeRootName)
```

Exchange Online PowerShellにリモート接続した上で、`GetGalStructure.ps1`を実行。

```powershell
PS > .\GetGalStructure.ps1
```

`$Datas`に階層構造のリストが格納されるので`Out-GridView`や`Export-Csv`でリストを出力すればよい。

`Out-GridView`で出力する場合。

```powershell
PS > $Datas | Out-GridView
```

Excelなどで加工して二次利用はしやすいが、パッと見で階層構造が分かりづらいかも。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258416/03802d9b-ac3a-3867-2fbc-128cb099979c.png)


## パッと見の分かりやすさ重視のスクリプト

スクリプトは以下の通り。

```GetGalStructure2.ps1
#getGroupTree関数定義
function getGroupTree($targetRecipient, $upperHierarchy){

    #現在の$targetRecipientを格納するオブジェクト$Dataを定義
    $Data = New-Object PSObject | Select-Object upperHierarchy, DisplayName, PrimarySmtpAddress, Title, RecipientType
    $Data.PrimarySmtpAddress = $targetRecipient.PrimarySmtpAddress
    $Data.RecipientType = $targetRecipient.RecipientType

    #$targetRecipientがグループの場合
    if($targetRecipient.RecipientType -eq "MailUniversalSecurityGroup"){

        #$upperHierarchyに$targetRecipientを追加
        $upperHierarchy += @($targetRecipient.DisplayName)

        #$Datasに$targetRecipientの情報を追記
        $Data.upperHierarchy = $upperHierarchy -join " - "
        [void]$Datas.Add($Data)

        #$targetRecipientのメンバー取得
        Get-DistributionGroupMember $targetRecipient.Alias |
            #取得したメンバーをユーザー、連絡先、グループの順で並び替え
            Select-Object Alias, DisplayName, PrimarySmtpAddress, Title, RecipientType, @{L="SortIndex"; E={switch($_.RecipientType){
                "UserMailBox"{0}
                "MailContact"{1}
                "MailUniversalSecurityGroup"{2}
            }}} |
            Sort-Object -Property SortIndex, DisplayName |
            #取得したメンバーに対して関数を再帰実行
            ForEach-Object {getGroupTree $_ $upperHierarchy}

    #$targetRecipientがその他受信者の場合
    }else{

        #$Datasに$targetRecipientの情報を追記
        $Data.DisplayName = $targetRecipient.DisplayName
        $Data.Title = $targetRecipient.Title
        [void]$Datas.Add($Data)

    }

}

#取得したGAL構造を格納するオブジェクト$Datasを定義
$global:Datas = New-Object System.Collections.ArrayList

#GALルート取得、チェック
$treeRootName = (Get-OrganizationConfig).HierarchicalAddressBookRoot
if([string]::IsNullOrEmpty($treeRootName)){"HierarchicalAddressBookRoot未指定。スクリプトの実行を中断します。";exit}

#GALルートに対してgetGroupTree関数を実行
getGroupTree (Get-DistributionGroup $treeRootName)
```

Exchange Online PowerShellにリモート接続した上で、`GetGalStructure2.ps1`を実行。

```powershell
PS > .\GetGalStructure2.ps1
```

こちらも同様に`$Datas`に階層構造のリストが格納されるので`Out-GridView`や`Export-Csv`でリストを出力すればよい。

`Out-GridView`で出力する場合。

```powershell
PS > $Datas | Out-GridView
```

先程のリストに比べてパッと見で階層構造が分かりやすいが、そのために出力を省いている列があるので、Excelなどで加工はしにくいかも。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258416/a5e966b9-9dcc-df4a-8a8c-e6b0bf642e14.png)

# おわりに

要望があれば`SeniorityIndex`も含むリストを出力するスクリプトも作って紹介するかも。（`Get-DistributionGroupMember`では`SeniorityIndex`プロパティが取得できないため、GALに含まれるオブジェクトごとに関数内でさらにGetコマンドを実行する必要があり、実行時の負担が結構大きい。）

以上