Terminating errors in Get-Team and Get-TeamUser breaks script

I’ve got a script to list all Teams in our tenant with their associated owners, members and guests.

It’s a large tenant with +4500 teams so it takes quite a bit of time to run through each team.

The problem is that even though I create a new CSV of Teams immediately before running the script it seems that I will encounter a team that for some reason or other has been removed.
And instead of continuing to the next team in the CSV Get-TeamUser will throw a terminating error and stop the script and lose all progress.
Initially I had Get-TeamUser without the -ErrorAction SilentlyContinue parameter, but I added that on one of the first failures of the script. However it seems that Get-TeamUser ignores that parameter.

Here’s the relevant part of the script:

  $Teams = Import-Csv -Path $TeamsCSV -Delimiter ';' -Encoding utf8BOM

  $CsvPath = "$((Get-Date -Format yyyyMMdd).ToString())-TeamsOwnerMemberGuest.csv"

  [array]$OwnerColl   = $null
  [array]$MemberColl  = $null
  [array]$GuestColl   = $null
  $ExportCsv = @()
  Write-Output $Teams.Count
  $i = 0

  foreach ($Team in $Teams) {
    Write-Output $Team.DisplayName
    $OwnerCount   = 0
    $MemberCount  = 0
    $GuestCount   = 0

    $OwnerColl = Get-TeamUser -GroupId $Team.GroupId -Role Owner -ErrorAction SilentlyContinue
    $OwnerCollection = @()
    foreach ($owner in $OwnerColl) {
      $OwnerCount++
      $OwnerCollection += $owner.User
    }
    Write-Output "`t# of owners: $($OwnerCount)"

    $MemberColl = Get-TeamUser -GroupId $Team.GroupId -Role Member -ErrorAction SilentlyContinue
    $MemberCollection = @()
    foreach ($member in $MemberColl) {
      $MemberCount++
      $MemberCollection += $member.User
    }
    Write-Output "`t# of members: $($MemberCount)"

    $GuestColl = Get-TeamUser -GroupId $Team.GroupId -Role Guest -ErrorAction SilentlyContinue
    $GuestCollection = @()
    foreach ($guest in $GuestColl) {
      $GuestCount++
      $GuestCollection += $guest.User
    }
    Write-Output "`t# of guests: $($GuestCount)"

    # Create a PSCustomObject for each Team with Owners, members and guests
    $TeamRecord = [PSCustomObject]@{
      GroupId       = $Team.GroupId
      Name          = $Team.DisplayName
      OwnerCount    = $OwnerCount
      MemberCount   = $MemberCount
      GuestCount    = $GuestCount
      Owners        = $OwnerCollection -join ' | '
      Members       = $MemberCollection -join ' | '
      Guests        = $GuestCollection -join ' | '
    }
    # Add the TeamRecord to the export array
    $ExportCsv += $TeamRecord

    $i++
    Write-Progress -Activity 'Exporting Teams' -Status "Exported $i of $($Teams.Count) teams" -PercentComplete (($i/$Teams.Count) * 100)
  }
  $ExportCsv | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding utf8BOM -Delimiter ';'

(I’m aware that the += is not ideal for adding to arrays, as it destroys and creates new arrays for each time through the loop. I will have to look at rewriting that, but I need to get this working)

I thought I could perhaps wrap each of the Get-TeamUser-blocks in something like this:

if ((Get-TeamUser -GroupId $Team.GroupId -Role 'Owner' -ErrorAction SilentlyContinue) -eq $null) { 
    write-output 'No owners... Continuing the script' 
}
else {
	$OwnerCollection = @()
	foreach ($owner in $OwnerColl) {
		$OwnerCount++
		$OwnerCollection += $owner.User
	}
	Write-Output "`t# of owners: $($OwnerCount)"
}

However, testing this outside of the script seems to just show that terminating error blows up the if as well and terminates everything.

My next idea was to start the Foreach with a check of each Team with the Get-Team cmdlet, but it seems that also throws a terminating error independent of whether I add the -ErrorAction SilentlyContinue parameter.

I’ll happily take any thoughts and suggestions you may have.

1 Like

Use try/catch:

try {
    Get-TeamUser -GroupId 'IAmNotAGroup' -ErrorAction 'Stop'
}
catch {
    Write-Warning "Error Getting Group."
}
1 Like

I’m obviously not on top of my game… I was thinking about this all wrong.

I was thinking by forcing the terminitaing error in a try/catch I would be doing what’s happening now… Basically ending the script, but of course having the catch “locally” for lack of a better word, the script would continue after the try/catch block.

Thank you.

Try/Catch was of course the solution.

I wrapped the Try about most of the code in the ForEach loop and added a Get-Team at the very start to catch the error as early as possible.
(Is there an elegant/POSH way of suppressing the output of a cmdlet? Like a POSH version of /dev/null? I know I can assign it to a variable that I never use later, but that just seems wrong.)

  $Teams = Import-Csv -Path $TeamsCSV -Delimiter ';' -Encoding utf8BOM

  $CsvPath = "$((Get-Date -Format yyyyMMdd).ToString())-TeamsOwnerMemberGuest.csv"
  $ErrorPath = "$((Get-Date -Format yyyyMMdd).ToString())-TeamsOwnerMemberGuestError.csv"

  [array]$OwnerColl   = $null
  [array]$MemberColl  = $null
  [array]$GuestColl   = $null
  $ExportCsv = @()
  $ErrorLog = @()
  Write-Output $Teams.Count
  $i = 0

  foreach ($Team in $Teams) {
    try {
      # Check if team exists and continue if it does.
      Get-Team -GroupId $Team.GroupId -ErrorAction Stop

      Write-Output $Team.DisplayName
      $OwnerCount   = 0
      $MemberCount  = 0
      $GuestCount   = 0

      $OwnerColl = Get-TeamUser -GroupId $Team.GroupId -Role Owner -ErrorAction SilentlyContinue
      $OwnerCollection = @()
      foreach ($owner in $OwnerColl) {
        $OwnerCount++
        $OwnerCollection += $owner.User
      }
      Write-Output "`t# of owners: $($OwnerCount)"

      $MemberColl = Get-TeamUser -GroupId $Team.GroupId -Role Member -ErrorAction SilentlyContinue
      $MemberCollection = @()
      foreach ($member in $MemberColl) {
        $MemberCount++
        $MemberCollection += $member.User
      }
      Write-Output "`t# of members: $($MemberCount)"

      $GuestColl = Get-TeamUser -GroupId $Team.GroupId -Role Guest -ErrorAction SilentlyContinue
      $GuestCollection = @()
      foreach ($guest in $GuestColl) {
        $GuestCount++
        $GuestCollection += $guest.User
      }
      Write-Output "`t# of guests: $($GuestCount)"

      # Create a PSCustomObject for each Team with Owners, members and guests
      $TeamRecord = [PSCustomObject]@{
        GroupId       = $Team.GroupId
        Name          = $Team.DisplayName
        OwnerCount    = $OwnerCount
        MemberCount   = $MemberCount
        GuestCount    = $GuestCount
        Owners        = $OwnerCollection -join ' | '
        Members       = $MemberCollection -join ' | '
        Guests        = $GuestCollection -join ' | '
      }
      # Add the TeamRecord to the export array
      $ExportCsv += $TeamRecord

      $i++
      Write-Progress -Activity 'Exporting Teams' -Status "Exported $i of $($Teams.Count) teams" -PercentComplete (($i/$Teams.Count) * 100)
    }
    catch {
      Write-Warning "Team $($Team.DisplayName) does not exist"
      # Create a PSCustomObject for each Team with error message
      $ErrorLog += [PSCustomObject]@{
        DateTime      = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        GroupId       = $Team.GroupId
        Name          = $Team.DisplayName
        ErrorMessage  = $_.Exception.Message
      }
    }
  }
  $ExportCsv | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding utf8BOM -Delimiter ';'
  $ErrorLog | Export-Csv -Path $ErrorPath -NoTypeInformation -Encoding utf8BOM -Delimiter ';'

I also added a separate ErrorLog for the Catch-block so any errors can be checked later, rather than just be a yellow line or two on the screen among more than 4000 other lines output to the console.

You mean like

? :wink:

Or this …

1 Like

Yes, honestly that was just me being lazy… :grinning:
After submitting that I just googled it myself and that line is already edited to look like this now:

Get-Team -GroupId $Team.GroupId -ErrorAction Stop | Out-Null