PowerShellを使ってアイテム保持ポリシーに保持対象を追加するときの注意点

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/a936dfce-a97f-fc79-2da6-2d3ec5c3f9d8.png)

アイテム保持ポリシーに保持対象を一括追加するスクリプトを設計します。「保持対象のアイテムごとに保持ポリシーを指定する」という要件があったとしたら、スクリプトのインプット情報として上の図のようなCSVを使うことをぱっと思いつくと思います。

このときの注意点を備忘を兼ねて投稿します。
#主旨
上記のようなCSVを使ってアイテム保持ポリシーへの保持対象の追加を正しく行うことがこの投稿の主旨です。

#発生したエラーと対処方法
CSVに記載している保持対象に追加したいアイテムを、以下のようなPowerShellを使って保持ポリシーに追加しようとします。
<font color="Red">結論としてこのソースコードではエラーになります。</font>

```powershell:保持ポリシーの追加に失敗するソースコード
#----------------------------------------------------
#コンプライアンスセンターに接続
#----------------------------------------------------
$user = "XXX@XXX.onmicrosoft.com"
$pass = "XXX" | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $user,$pass

#未接続であれば接続コマンドを実行する
$PSSession   = Get-PSSession | Select-Object -Property State,Name
$isConnected = ((@($PSSession) -like '@{State=Opened; Name=ExchangeOnlineInternalSession*').Count -gt 0)
If ($isConnected -eq $false) 
{
    #Exchange Online管理モジュールの使用を開始する
    Connect-ExchangeOnline -Credential $credential

    #コンプライアンスセンターに接続する
    Connect-IPPSSession -UserPrincipalName $user
}

#----------------------------------------------------
#CSVから読み込んだサイト一覧を保持ポリシーに追加する
#----------------------------------------------------
$csv = Import-Csv -Path .\SharePointSites.csv -Encoding UTF8

foreach($item in $csv)
{
    Set-RetentionCompliancePolicy -AddSharePointLocation $item.Url -Identity $item.policy
}

```
実行すると下図のエラーが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/35fad503-c21c-25b1-a19c-add4eb97d085.png)
エラーメッセージをテキストに起こしたのが以下のものです。メッセージを読むと「前の変更処理が完了したら、次の処理が行える」と記載されているのが分かります。

つまり、エラーが発生した原因は「同じポリシーに対して間髪入れずに追加の処理を行ったこと」です。
<font color="Red">
Previous changes to the policy 'XXXX-XXXX-XXX-XXXX-XXXX' are being deployed. Once deployed, additional actions can be performed. Please refer to 'https://go.microsoft.com/fwlink/?linkid=2148779' to learn more.
    + CategoryInfo          : InvalidOperation: (:) [Set-RetentionCompliancePolicy]、PolicyLockConflictException
    + FullyQualifiedErrorId : [Server=OS2JPN01WS009,RequestId=18c5d1ba-1eaa-4e4b-a4f2-28ac37627a3f,TimeStamp=2021/11/01 12:38 
   :01] [FailureCategory=Cmdlet-PolicyLockConflictException] F2063744,Microsoft.Office.CompliancePolicy.Tasks.SetRetentionCompliancePolicy
    + PSComputerName        : jpn01b.ps.compliance.protection.outlook.com

</font>

#解決方法
このエラーを回避する方法は「ひとつのポリシーに対して複数の保持対象を追加する場合は、対象となる複数の追加対象を配列にしてSetコマンドに渡す」です。

Docsを見るとわかる通り、保持対象を追加するオプションの値は MultivaluedProperty です。つまり、複数の対象を指定することができます。
追加したい対象を配列にしてこのオプションに渡すとアイテムの個数に関わらずコマンドの実行を1回で済ませることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/5e4f31aa-dfe6-8a68-6b70-ca430efdc21e.png)
参照先：
https://docs.microsoft.com/ja-JP/powershell/module/exchange/set-retentioncompliancepolicy?view=exchange-ps

CSVからインポートした複数のアイテムを加工して、追加先のポリシーが同じものを配列としてまとめるにはどうすればいいでしょうか。

おそらく一番簡単な方法は Group-Object を使うことです。
Group-Object を使って手直ししたソースコードが以下の通りです。

```powershell:保持ポリシーの追加に成功するソースコード
#----------------------------------------------------
#コンプライアンスセンターに接続
#----------------------------------------------------
$user = "XXX@XXX.onmicrosoft.com"
$pass = "XXX" | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $user,$pass

#未接続であれば接続コマンドを実行する
$PSSession   = Get-PSSession | Select-Object -Property State,Name
$isConnected = ((@($PSSession) -like '@{State=Opened; Name=ExchangeOnlineInternalSession*').Count -gt 0)
If ($isConnected -eq $false) 
{
    #Exchange Online管理モジュールの使用を開始する
    Connect-ExchangeOnline -Credential $credential

    #コンプライアンスセンターに接続する
    Connect-IPPSSession -UserPrincipalName $user
}

#----------------------------------------------------
#CSVから読み込んだサイト一覧を保持ポリシーに追加する
#----------------------------------------------------
$csv = Import-Csv -Path .\SharePointSites.csv -Encoding UTF8

$itemsGroupedByPolicy = $csv | Group-Object -Property policy 

foreach($group in $itemsGroupedByPolicy)
{
    $targetSites = $group.Group | select -ExpandProperty Url

    Set-RetentionCompliancePolicy -AddSharePointLocation $targetSites -Identity $group.Name
}
```
#最後に
保持ポリシーへの追加は「新規にユーザーを作ったとき」や「新しいチームを作ったとき」の行う設定のひとつになる場合があるので、自動化しようとしてこのエラーに阻まれたひとはいるかと思います。

エラーメッセージで検索したどなたかの助けになれば幸いです。