Azure Runbook failing on connection to Exchange Online and Teams

A runbook for creating Teams in my tenant stopped working after Microsoft disabled Basic Authentication.
The runbook was created for us by a consultant before I started in the organization, and I have no way of contacting this person.

I’m trying to refactor the script to use a ServicePrincipal instead of Credentials, based on another runbook which works as intended. I’m aware that Managed Identities is the way forward, but I need to get this up and running as fast as possible.

However, I’m bumping into a number of issues with the updated/refactored script.

Here’s an anonymized version of the script:

#Requires -Version 5.1
#Requires -Modules AzureAD, ExchangeOnlineManagement, MicrosoftTeams
[CmdLetBinding()]
Param(
  [string]$description, #Please describe the use of this team in short text
  [string]$iso, #ISO Country code or global name
  [string]$sitename, #User selected TeamsName
  [string]$classification, #Classification of the Team
  [string]$teamsname, #The real TeamsName
  [string]$alias, #Alias / MailNickName of the Teams
  [string]$privacysettings, #Private or Public Teams
  [string]$teamowner, #The email address of the owner of the Team
  [string]$secowner #The email address of the secondary owner of the Team
)
<############################
  Connnection Phase
############################>
Write-Output 'Running version 1.1'
# $ConnectionName = 'AzureRunAsConnection'
$ServicePrincipalConnection = Get-AutomationConnection -Name 'AzureRunAsConnection'
# $Credential = Get-AutomationPSCredential -Name 'PS-CreateTeams'

try {
  Write-Output 'Connecting to MS Teams'
  $TeamsParams = @{
    CertificateThumbprint   = $ServicePrincipalConnection.CertificateThumbprint
    ApplicationId           = $ServicePrincipalConnection.ApplicationId
    TenantId                = $ServicePrincipalConnection.TenantId
    ErrorAction             = 'Stop'
  }
  Connect-MicrosoftTeams @TeamsParams

  Write-Output 'Connecting to AzureAD'
  $AzureADParams = @{
    CertificateThumbprint   = $ServicePrincipalConnection.CertificateThumbprint
    ApplicationId           = $ServicePrincipalConnection.ApplicationId
    TenantId                = $ServicePrincipalConnection.TenantId
    ErrorAction             = 'Stop'
  }
  Connect-AzureAd @AzureADParams

  Write-Output 'Connecting to Exchange Online'
  $ExOnlineParams = @{
    CertificateThumbprint   = $ServicePrincipalConnection.CertificateThumbprint
    AppID                   = $ServicePrincipalConnection.ApplicationId
    Organization            = 'MyOrganisation.onmicrosoft.com'
    CommandName             = 'Get-UnifiedGroup','Set-UnifiedGroup'
    ErrorAction             = 'Stop'
  }
  Connect-ExchangeOnline @ExOnlineParams

  Write-Output 'Connecting to Exchange Online: Security & Compliance Center'
  Connect-IPPSSession @ExOnlineParams
}
catch {
  Write-Error -Message $_.Exception.Message
  Disconnect-MicrosoftTeams | Out-Null
  Disconnect-AzureAD | Out-Null
  Disconnect-ExchangeOnline | Out-Null
  Get-PSSession | Remove-PSSession | Out-Null
  Break
}
Write-Output 'Connection to AzureAD, MicrosoftTeams, ExchangeOnline and Security & compliance successful!'
<############################
  End connnection Phase
############################>

<############################
  Team Creation Phase
############################>
try {
  Write-Output "Creating team: $($teamsname)"
  $newteam = New-Team -MailNickName $alias -DisplayName $teamsname -Description $description -Owner $teamowner -Visibility Private -AllowCreateUpdateChannels $false
  Add-TeamUser -GroupId $newteam.GroupId -User $secowner -Role Owner
}
catch {
  Write-Error -Message "Unable to create Team: $($teamsname)"
  Write-Error -Message $_.Exception.Message
  Disconnect-MicrosoftTeams | Out-Null
  Disconnect-AzureAD | Out-Null
  Disconnect-ExchangeOnline | Out-Null
  Get-PSSession | Remove-PSSession | Out-Null
  Break
}
Write-Output "Team $($teamsname) created."

$Retry = 1
Start-Sleep -Seconds 10
try {
  Write-Output 'Get UnifiedGroup and hide it from GAL and set SensitivityLabelId'
  do {
    if ($Group = Get-UnifiedGroup -Filter "alias -eq '$alias" -ErrorAction Stop -Verbose) {
      Write-Output 'Found Office 365 group.'
      $Group | Set-UnifiedGroup -HiddenFromAddressListsEnabled $true
      if ($classification -eq 'Internal') {
        Write-Output 'Match classification: Internal'
        $Group | Set-UnifiedGroup -SensitivityLabelId "InternalLabelId"
        $Retry = 6
      }
      elseif ($classification -eq 'Open') {
        Write-Output 'Match classification: Open'
        $Group | Set-UnifiedGroup -SensitivityLabelId "OpenLabelId"
        $Retry = 6
      }
    }
    else {
      Write-Output "Unable to find Office 365 group. ($($Retry))"
      Start-Sleep -Seconds 10
      $Retry ++
    }
  }
  while ($Retry -le 5)
}
catch {
  Write-Error -Message $_.Exception.Message
  throw $_.Exception
}
Write-Output "Settings for team $($teamsname) is done."
Write-Output $newteam | ConvertTo-Json
<############################
  End team Creation Phase
############################>

<############################
  Cleanup Phase
############################>
Write-Output 'Exiting script and ending connections to AzureAD, MicrosoftTeams and ExchangeOnline'
Disconnect-MicrosoftTeams | Out-Null
Disconnect-AzureAd | Out-Null
Disconnect-ExchangeOnline | Out-Null
Get-PSSession | Remove-PSSession | Out-Null
<############################
  End Cleanup Phase
############################>

Here are the issues I’m seeing:

  • The ExchangeOnline connection string fails with the following error:
UnAuthorized
System.Management.Automation.MethodInvocationException: Exception calling "ShouldProcess" with "3" argument(s): "A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: Press(Y/y/A/a) if you want to continue." ---> System.Management.Automation.Host.HostException: A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: Press(Y/y/A/a) if you want to continue.
   at System.Management.Automation.Internal.Host.InternalHostUserInterface.ThrowPromptNotInteractive(String promptMessage)
   at System.Management.Automation.Internal.Host.InternalHostUserInterface.PromptForChoice(String caption, String message, Collection`1 choices, Int32 defaultChoice)
   at System.Management.Automation.MshCommandRuntime.InquireHelper(String inquireMessage, String inquireCaption, Boolean allowYesToAll, Boolean allowNoToAll, Boolean replaceNoWithHalt, Boolean hasSecurityImpact)
   at System.Management.Automation.MshCommandRuntime.DoShouldProcess(String verboseDescription, String verboseWarning, String caption, ShouldProcessReason& shouldProcessReason)
   at System.Management.Automation.MshCommandRuntime.ShouldProcess(String verboseDescription, String verboseWarning, String caption)
   at System.Management.Automation.Cmdlet.ShouldProcess(String verboseDescription, String verboseWarning, String caption)
   at CallSite.Target(Closure , CallSite , PSScriptCmdlet , String , String , String )
   --- End of inner exception stack trace ---
   at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
   at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.LightLambda.RunVoid1[T0](T0 arg0)
   at System.Management.Automation.PSScriptCmdlet.RunClause(Action`1 clause, Object dollarUnderbar, Object inputToProcess)
   at System.Management.Automation.PSScriptCmdlet.DoProcessRecord()
   at System.Management.Automation.CommandProcessor.ProcessRecord()
  • Though Connect-MicrosoftTeams line seems to work, and I see that it’s connected the script fails on the Create-Teams line with the following error:
Unable to create Team: Test-STA
You must call the Connect-MicrosoftTeams cmdlet before calling any other cmdlets.
System.Management.Automation.MethodInvocationException: Exception calling "ShouldProcess" with "3" argument(s): "A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: Press(Y/y/A/a) if you want to continue." ---> System.Management.Automation.Host.HostException: A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: Press(Y/y/A/a) if you want to continue.
  • It seems as though the ExchangeOnlineManagement module does not expose the Get-UnifiedGroup and Set-UnifiedGroup cmdlets. When the script tries to hide the groups from the Global Address List and set the Sensitivity Label, I get this error:
The term 'Get-UnifiedGroup' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

A minor annoyance is that I’ve added ‘Break’ into my Try/Catch blocks and set the ErrorAction to Stop in the connections, but when the script fails it seems to just continue on regardless.

I’ve added the Teams, Exchange and Compliance Administrator roles to the ServicePrincipal so it should have the permissions it needs in the Tenant.

I’ve tried connecting to ExchangeOnline with both the splat shown in the script and as a line with each parameter defined separately. And when running it as just a line I tried omitting the CommandName and ErrorAction parameters. But I get the same connection error.