PowerShellからTeamsを操作する

# 背景

業務上の理由でmicrosoft365関係の操作をコマンドラインから管理する方法を備忘録として残す．Teamsのメンバー登録などの管理が主な理由である．手動による管理以外にはPowerShellを利用した方法しか現状toolが存在しないようである．

なおPowerShellの環境はsshで接続できるように[前回の記事](https://qiita.com/hanaata/items/d9b5d0ddf2c6c72275eb)で環境構築したが，microsoft365へのログインでGUI環境（ブラウザが起動）が必須であり，ssh経由で認証が通らないため全く利用価値がなかった（）．

[macでもpowershellをinstallできる](https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.4)ようなのでhomebrew経由でinstallする

```:bash
$ brew install powershell/tap/powershell
```

```:bash 
$ pwsh
```

PowerShellのversionは

```:PowerShell
PS /Users/hanaata> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.6
PSEdition                      Core
GitCommitId                    7.4.6
OS                             Darwin 24.0.0 Darwin Kernel Version 24.0.0: Tue…
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
```

windows でも macでもPowerShell v7以降では補完機能が利用できるのでv7以上を利用することを勧める．

# 1 必要なツールをinstallする

## 1.1 msgraph-sdk-powershellをinstallする

Microsoft365の管理は[msgraph-sdk-powershell](https://github.com/microsoftgraph/msgraph-sdk-powershell)を利用するようで，sdkのインストールはPowerShellから

```:PowerShell
PS /Users/hanaata> Install-Module Microsoft.Graph

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
 InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install
the modules from 'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): Y
```

でinstall可能．installされたversionの確認は

```:PowerShell
PS /Users/hanaata> Get-InstalledModule Microsoft.Graph

Version              Name                                Repository           Description
-------              ----                                ----------           -----------
2.25.0               Microsoft.Graph                     PSGallery            Microsoft Graph PowerShell module
```

## 1.2 Microsoft Teams PowerShellをinstallする．

[公式サイト](https://learn.microsoft.com/ja-jp/microsoftteams/teams-powershell-install)を参考にTeams PowerShellモジュールをインストールする．

```:PowerShell
PS /Users/hanaata> Install-Module -Name PowerShellGet -Force -AllowClobber
PS /Users/hanaata> Install-Module -Name MicrosoftTeams -Force -AllowClobber
```

## 1.3 Microsoft365にサインインする

下記コマンドでサインインできる（ブラウザが立ち上がってサインインを求められるので，ssh経由で認証が通らない）

```:PowerShell
PS /Users/hanaata> Connect-MicrosoftTeams

Account              Environment Tenant                               TenantId
-------              ----------- ------                               --------
hanaata@qiita.com   xxxxxxxxx    xxxxx-xxx-xxx-xxxx                   xxxx-xxx-xxx-x
```

## 1.4 teams moduleのupdate

マニュアルに沿ってteams moduleをupdateしておく．

```:PowerShell
PS /Users/hanaata> Update-Module MicrosoftTeams
```

# 2 PowerShellを使ってTeamsを管理する．

## 2.1 コマンドマニュアルの探し方

[公式のマニュアル](https://learn.microsoft.com/en-us/powershell/module/teams/?view=teams-ps) で左にあるFilter by titleから `Add` とか `Get` とか `Remove` とか 検索すると良い．

![スクリーンショット 2025-01-17 13.22.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138934/4ebb0478-9914-237a-9744-47d0bf011d00.png)


## 2.2 チームを作成する

[公式サイト](https://learn.microsoft.com/ja-jp/microsoftteams/teams-powershell-managing-teams) を参考にする．

> コア チームとその設定 (new-team、 get-team、 set-team)、チーム ユーザーの管理 (add-teamuser、 remove-teamuser)、およびチームのチャネル (new-teamchannel、 remove-teamchannel) を管理するためのコマンドレットのセットが用意されています。

とあるのでこの辺りのコマンドが利用できそう．とりあえずマニュアルにあるコマンドを実行してみる．

```
PS /Users/hanaata> New-Team -DisplayName "Contoso Marketing" -Description "Collaboration space for Contoso's Marketing department"

GroupId                              DisplayName        Visibility  Archived  MailNickName       Description
-------                              -----------        ----------  --------  ------------       -----------
87d3cbc5-f45e-4042-8e1a-9f8b97a47b74 Contoso Marketing  Private     False     msteams_ae4a41     Collaboration spa…
```

そうするとTeams.appでチームを確認すると`Contoso Marketing`というチームが作成されていることが確認できる．チームの所有者は作成者である．

## 2.3 チームにメンバーを追加・削除する．

[Add-TeamUser](https://learn.microsoft.com/en-us/powershell/module/teams/add-teamuser?view=teams-ps)で可能．

一つ前で作成したチーム（GroupId:87d3cbc5-f45e-4042-8e1a-9f8b97a47b74）にユーザーを追加する（user@qiita.com）

```
PS /Users/hanaata> Add-TeamUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -User user@qiita.com
```

削除する場合は[Remove-TeamUser](https://learn.microsoft.com/en-us/powershell/module/teams/remove-teamuser?view=teams-ps)で可能．



## 2.4 Channelを作成する．

[New-TeamChannel](https://learn.microsoft.com/en-us/powershell/module/teams/new-teamchannel?view=teams-ps)で実現可能．オプションで `-MembershipType Private` をつければPrivateChannelを作成可能．

```
PS /Users/hanaata> New-TeamChannel -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -DisplayName test1

Id                                               DisplayName Description MembershipType
--                                               ----------- ----------- --------------
19:5b307e95b9074f6d9bc5a158b3bb376c@thread.tacv2 test1                         Standard

PS /Users/hanaata> New-TeamChannel -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -DisplayName test2 -MembershipType Private

Id                                               DisplayName Description MembershipType
--                                               ----------- ----------- --------------
19:ee7b970797214dd19c42770bfe6c62d7@thread.tacv2 test2                          Private
```

## 2.5 ChannelにUserを追加する．

[Add-TeamChannelUser](https://learn.microsoft.com/en-us/powershell/module/teams/add-teamchanneluser?view=teams-ps)で可能．
チャンネルのへの追加はprivateのみ可能でstandardは不可．

```
PS /Users/hanaata> Add-TeamChannelUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -DisplayName "test1" -User user@qiita.com
Add-TeamChannelUser: Operation not allowed on channel of type Standard.
PS /Users/hanaata> Add-TeamChannelUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -DisplayName "test2" -User user@qiita.com
```

## 2.6 group idを取得する

[Get-Team](https://learn.microsoft.com/en-us/powershell/module/teams/get-team?view=teams-ps)で可能．`Get-Team` だけでも構わないが，アクセス権のないTeamも[Error retrieving Team with GroupId [xxx]. ErrorCode: [Forbidden]. ErrorMessage: Failed to execute Skype backend request GetFilteredThreadRequest.] となるため自身が所属するチームだけ出力した方が視認性は良いであろう．

```
PS /Users/hanaata> Get-Team -User hanaata@qiita.com

GroupId                              DisplayName        Visibility  Archived  MailNickName       Description
-------                              -----------        ----------  --------  ------------       -----------
5248d39c-09fc-487e-bd0c-18f3ab40e979 Contoso Marketing  Private     False     msteams_ae4a41     Collaboration spa…
```

# 3. PowerSehllでforを回す．

ChatGPTの助けを借りてpowershellスクリプトを作成する．基本形は下記のとおりで，Import-Csvを使ってcsvファイルを変数`$Users`に格納し，foreachでloopさせる．

```test.ps1
# 1. チームの GroupId を指定してください
$GroupId = "5248d39c-09fc-487e-bd0c-18f3ab40e979"

# 2. CSV ファイルのパスを指定してください
$CsvFilePath = "C:\Path\To\users.csv"

# 3. CSV ファイルをインポート
$Users = Import-Csv -Path $CsvFilePath

# 4. 各ユーザーをチームに追加
foreach ($User in $Users) {
    try {
        Add-TeamUser -GroupId $GroupId -User $User.UserEmail
        Write-Host "Added $($User.UserEmail) to the team successfully." -ForegroundColor Green
    } catch {
        Write-Host "Failed to add $($User.UserEmail): $_" -ForegroundColor Red
    }
}
```

```member.csv
氏名,M365アカウント,チャンネル名
ななし,test@aaa.com,bbc
test1,user1@qiita.com,プライベート
test2,user2@qiita.com,プライベート
test3,user3@qiita.com,プライベート
```

`test1.ps` だとユーザーをチームに登録するだけなのでユーザーをチームに登録する．~~のちにプライベートチャンネルへ登録するため，for文を~~チャンネルへの登録が不要な場合があるので，チームへの登録とチャンネルへの登録はスクリプトを分ける．

```
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
        Add-TeamUser -GroupId $GroupId -User $User.M365アカウント
    	$SuccessCount += 1
    	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）へ追加しました．"

    } catch {
        Write-Host "$($User.氏名) $($User.M365アカウント): のチーム（$($Team.DisplayName)）への追加に失敗しました．" -ForegroundColor Red
    	Write-Output "$($User.氏名),$($User.M365アカウント)" >> $Log
    	$FailCount += 1
    }
}
```

のようにする．600名程度の登録に5-10分かかる．おおむね１ユーザーあたり1秒かかる．遅い．

## 3.1 csvに基づきチームにユーザーを追加する．

下記スクリプトを作成した．

```AddUsers2Team.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\AddUsers2Team.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:hanaata@qiita.com)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "failed_addusers2team.log"
Write-Output "氏名,M365アカウント" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
        Add-TeamUser -GroupId $GroupId -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）へ追加しました．"

    } catch {
        Write-Host "$($User.氏名) $($User.M365アカウント): のチーム（$($Team.DisplayName)）への追加に失敗しました．" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチームに追加しました．失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "`n`t PS> cat $($Log)`n"
```

のようにしておけば下記コマンドでmember.csvに基づいてチームにユーザーを登録できる．

```:PowerShell
PS /Users/hanaata> ./AddUsers2Team.ps1 5248d39c-09fc-487e-bd0c-18f3ab40e979 ./member.csv
```

でユーザーの登録ができるようになる．登録に失敗したユーザーのログは"failed_addusers2team.log"に格納される．

約600名の学生のチャンネルの登録はチームへの登録より圧倒的に遅い．
１ユーザーあたり４〜５秒かかる．くっそ遅い．600名程度の登録が必要だが，その場合は1時間を見越したほうがいいかも．


## 3.2 csvに基づきプライベートチャンネルにユーザーを登録する．

AddUsers2Team.ps1とほとんど同一であるが下記スクリプトでユーザーをプライベートチャンネルに登録する．
（standardなチャンネルにはユーザーは登録できないようである）

```AddUsers2Channel.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\AddUsers2Channel.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:hanaata@qiita.com)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "adduser2channel_failed.log"
Write-Output "氏名,M365アカウント,チャンネル名" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
	Add-TeamChannelUser -GroupId $GroupId -DisplayName $User.チャンネル名 -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）のチャンネル（$($User.チャンネル名)）へ追加しました．"

    } catch {
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）のチャンネル（$($User.チャンネル名)）が失敗しました．" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント),$($User.チャンネル名)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチャンネルに追加しました．失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "`n`t PS> cat $($Log)`n"%
```

```:PowerShell
PS /Users/hanaata> ./AddUsers2Channel.ps1 5248d39c-09fc-487e-bd0c-18f3ab40e979 ./member.csv
```

でユーザーの登録ができるようになる．登録に失敗したユーザーのログは"failed_addusers2team.log"に格納される．

# 4 ユーザーをチームから削除する．

## 4.1 削除対象者のリストを生成する．

ユーザーの削除は

```
PS /Users/hanaata/> Remove-TeamUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979 -User test1@qiita.com
```

のようにして可能である．ここでは，チーム（5248d39c-09fc-487e-bd0c-18f3ab40e979）に登録されたユーザーの一覧を取得してcsvに出力して，csvに基づきユーザーを削除する．

まずは，[Get-TeamUser](https://learn.microsoft.com/ja-jp/powershell/module/teams/get-teamuser?view=teams-ps)で登録されたユーザーの一覧を取得する．


```
PS /Users/hanaata> Get-TeamUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979
UserId              User                 Name       Role
------              ----                 ----       ----
xxxxxxxxxxxxxxxxxxx hanaata@quiita.com hanaata      owner
yyyyyyyyyyyyyyyyyyy test1@qiita.com    test1        member
zzzzzzzzzzzzzzzzzzz test2@qiita.com    test2        member
```

ここからUser列を取得したいので

```PowerShell
PS /Users/hanaata> $TeamAllUsers = Get-TeamUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979
PS /Users/hanaata> $Users = $TeamAllUsers | Select-Object -Property User
```

とすればチームに登録された全てのメールアドレスを取得し，Usersに格納する（最後の一人のownerは削除できないので注意）．私の場合は特定のドメインのメールアドレスのみを抽出し（学生），教員は削除したくないので，（chatgptに聞いて）grepを下記のようにして実現する．

```
$Users = $TeamAllUsers | Where-Object { $_.User -like "*@student.qiita.com" } | Select-Object -Property User
```

して削除したいユーザーのアドレスをcsvにして保存する．
property: Roleがowner以外をのgrepするには

```
$Users = $TeamAllUsers | Where-Object { $_.Role -ne "owner" } | Select-Object -Property User
```

のようにすれば良い．csvへの出力は下記のようにした．

```
Write-Output "M365アカウント"　> removeUser.csv
#$Users | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8

$Users | ForEach-Object {
    $_.User
} | Out-File -FilePath $OutputCsvPath -Encoding UTF8 -Append
```

下記コマンドでcsvに保存すると，値がダブルクオテーション"で囲われてしまうので，コメントアウトしてForEach-Objectでループを回している．
```
#$Users | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8
```

チームに登録された（*@student.qiita.com）の削除用csvの作成スクリプト全体は以下のようにした．

```Get-RemoveUsers4students.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$OutputCsvPath = "./removeUsers.csv" # デフォルトのCSV出力パス
)

# 引数が指定されているかチェック
if (-not $GroupId) {
    Write-Host "Usage: .\ExportTeamMembers.ps1 <GroupId> [OutputCsvPath]" -ForegroundColor Yellow
    exit 1
}

# $GroupIdの全チームメンバーを取得
try {
    $TeamAllUsers = Get-TeamUser -GroupId $GroupId
} Catch {
    Write-Host "Error: Failed to retrieve members of the team with GroupId: $GroupId" -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

# $TeamAllUsersのUser（MSアカウント）列で「@の右側のドメインがstudent.qiita.com」となるメールアドレスを取得する．
$Users = $TeamAllUsers | Where-Object { $_.User -like "*@student.qiita.com" } | Select-Object -Property User

# Csv にエクスポート

Write-Output "M365アカウント" > $OutputCsvPath
#$Users | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8

$Users | ForEach-Object {
    $_.User
} | Out-File -FilePath $OutputCsvPath -Encoding UTF8 -Append

Write-Host "`n学生の削除リストを $($OutputCsvPath) に保存しました．削除対象者のリストを確認するには下記コマンドから可能です．"
Write-Host "`n`t PS > cat $($OutputCsvPath)"
Write-Host "`n学生をチームから削除するには下記コマンドから可能です"
Write-Host "`n`t. PS> /RemoveUsers4Team.ps1 $GroupId $OutputCsvPath`n"
```

```:PowerShell
PS /Users/hanaata> ./Get-RemoveUsers4students.ps1 5248d39c-09fc-487e-bd0c-18f3ab40e979

削除リストを ./removeUsers.csv に保存しました．削除対象者のリストを確認するには下記コマンドから可能です．

	 PS > cat ./removeUsers.csv

学生をチームから削除するには下記コマンドから可能です

	PS> /RemoveUsers4Team.ps1 5248d39c-09fc-487e-bd0c-18f3ab40e979 ./removeUsers.csv
```

となり，削除対象のリストが`reveUsers.csv`に保存される．

## 4.2 csvに基づきユーザーをチームから削除する

スクリプトの詳細は [2 PowerShellを使ってTeamsを管理する．](#-2-PowerShellを使ってTeamsを管理する)とほとんど同一なのでスクリプトの中身だけ示す．

```RemoveUsers4Team.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\RemoveUsers4Team.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:yoshida@showa-u.ac.jp)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "failed_removeusers4team.log"
Write-Output "氏名,M365アカウント" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
	Remove-TeamUser -GroupId $GroupId -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）から削除しました"

    } catch {
        Write-Host "$($User.氏名) $($User.M365アカウント): のチーム（$($Team.DisplayName)）の削除に失敗しました．`n $_" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチームから削除しました．削除失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "なお，チームの最後の所有者は削除できません．"
Write-Host "`n`t PS> cat $($Log)`n"
Write-Host "現在のteamの登録者は以下のとおりです`n"
Get-TeamUser -GroupId $GroupId
```

コマンドは下記のようにして可能．

```:PowerShell
PS /Users/hanaata> ./RemoveUsers4Team.ps1 5248d39c-09fc-487e-bd0c-18f3ab40e979 ./remove.csv
```

# 備考 You must call the Connect-MicrosoftTeams cmdlet before calling any other cmdlets.

適当なコマンドを実行して
```
PS /Users/hanaata> Get-TeamUser -GroupId 5248d39c-09fc-487e-bd0c-18f3ab40e979
Get-TeamUser: You must call the Connect-MicrosoftTeams cmdlet before calling any other cmdlets.
```
のように出力される場合は
```
PS /Users/hanaata> Connect-MicrosoftTeams
```
からサインインしてください．サインインはパワーシェルを閉じたり，異なるタブで開いた場合に端末毎にサインインが必要です．


# 備考 作成したプログラム

macのterminalで
```
hanaata@qiita$ cat AddUsers2Team.ps1 | sed -e 's/user@domain/hanaata@qiita.com/g' | pbcopy
```
のようにすればクリップボードにスクリプトをコピペできる．

今回作成したスクリプトは下記の4つ．

```AddUsers2Team.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\AddUsers2Team.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:hanaata@qiita.com)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "failed_addusers2team.log"
Write-Output "氏名,M365アカウント" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
        Add-TeamUser -GroupId $GroupId -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）へ追加しました．"

    } catch {
        Write-Host "$($User.氏名) $($User.M365アカウント): のチーム（$($Team.DisplayName)）への追加に失敗しました．" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチームに追加しました．失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "`n`t PS> cat $($Log)`n"
```

```AddUsers2Channel.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\AddUsers2Channel.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:hanaata@qiita.com)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "adduser2channel_failed.log"
Write-Output "氏名,M365アカウント,チャンネル名" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
	Add-TeamChannelUser -GroupId $GroupId -DisplayName $User.チャンネル名 -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）のチャンネル（$($User.チャンネル名)）へ追加しました．"

    } catch {
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）のチャンネル（$($User.チャンネル名)）が失敗しました．" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント),$($User.チャンネル名)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチャンネルに追加しました．失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "`n`t PS> cat $($Log)`n"
```

```Get-RemoveUsers4students.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$OutputCsvPath = "./removeUsers.csv" # デフォルトのCSV出力パス
)

# 引数が指定されているかチェック
if (-not $GroupId) {
    Write-Host "Usage: .\ExportTeamMembers.ps1 <GroupId> [OutputCsvPath]" -ForegroundColor Yellow
    exit 1
}

# $GroupIdの全チームメンバーを取得
try {
    $TeamAllUsers = Get-TeamUser -GroupId $GroupId
} Catch {
    Write-Host "Error: Failed to retrieve members of the team with GroupId: $GroupId" -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

# $TeamAllUsersのUser（MSアカウント）列で「@の右側のドメインがs.showa-u.ac.jp」となるメールアドレスを取得する．
$Users = $TeamAllUsers | Where-Object { $_.User -like "*@s.showa-u.ac.jp" } | Select-Object -Property User

# Csv にエクスポート

Write-Output "M365アカウント" > $OutputCsvPath
#$Users | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8

$Users | ForEach-Object {
    $_.User
} | Out-File -FilePath $OutputCsvPath -Encoding UTF8 -Append

Write-Host "`n学生の削除リストを $($OutputCsvPath) に保存しました．削除対象者のリストを確認するには下記コマンドから可能です．"
Write-Host "`n`t PS > cat $($OutputCsvPath)"
Write-Host "`n学生をチームから削除するには下記コマンドから可能です"
Write-Host "`n`t PS> /RemoveUsers4Team.ps1 $GroupId $OutputCsvPath`n"
```

```RemoveUsers4Team.ps1
# スクリプトの引数
param (
    [string]$GroupId,
    [string]$CsvFilePath
)

# 引数が指定されているかチェック
if (-not $GroupId -or -not $CsvFilePath) {
    Write-Host "Usage: .\RemoveUsers4Team.ps1 <GroupId> <CsvFilePath>" -ForegroundColor Yellow
    exit 1
}

# CSV ファイルが存在するかチェック
if (-not (Test-Path -Path $CsvFilePath)) {
    Write-Host "Error: The file '$CsvFilePath' が存在しません." -ForegroundColor Red
    exit 1
}


Try {
    # 指定された GroupId のチーム情報を取得
    $Team = Get-Team -GroupId $GroupId
} catch {
    # エラーが発生した場合の処理
    Write-Host "Error: グループ '$GroupId' が存在しません．
    チームのGroupIdを確認してください．チームのGroupIdを確認する方法は下記の通りです．

    PS > Get-Team -User <マイクロソフトアカウント (例:hanaata@qiita.com)>
    " -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red

    # スクリプトを終了
    exit 1
}

# CSV ファイルをインポート
try {
    $Users = Import-Csv -Path $CsvFilePath
} Catch {
    Write-Host "Error: CSV を読み込めません．'$CsvFilePath'." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

$SuccessCount = 0
$FailCount = 0

$Log = "failed_removeusers4team.log"
Write-Output "氏名,M365アカウント" > $Log
Write-Host ""

# for文で$Usersを$Userに代入してloopさせる
foreach ($User in $Users) {
    try {
        # チームにユーザーを追加
	Remove-TeamUser -GroupId $GroupId -User $User.M365アカウント
	$SuccessCount += 1
	Write-Host "$($User.氏名)（ $($User.M365アカウント) ）をチーム（$($Team.DisplayName)）から削除しました"

    } catch {
        Write-Host "$($User.氏名) $($User.M365アカウント): のチーム（$($Team.DisplayName)）の削除に失敗しました．`n $_" -ForegroundColor Red
	Write-Output "$($User.氏名),$($User.M365アカウント)" >> $Log
	$FailCount += 1
    }
}

Write-Host "`n Summary:"
Write-Host "`t $($SuccessCount) 名をチームから削除しました．削除失敗は $($FailCount) 件あります．`n`t 次のコマンドで登録に失敗したユーザー（$($Log)）の出力確認してください．`n"
Write-Host "なお，チームの最後の所有者は削除できません．"
Write-Host "`n`t PS> cat $($Log)`n"
Write-Host "現在のteamの登録者は以下のとおりです`n"
Get-TeamUser -GroupId $GroupId
```