microsoft teams
11 TopicsWhen creating a new team from a template with powershell add new private channel and members
Hi All, I have a powershell script I am using to create and populate new teams from a template and add owners and users via .csv, Everything seem to work fine except the private team in the template is not copied to the new teams. Is there a way to copy the private team with its members from the template? if not how can I add a new private team and add users from a .csv file to my existing script. Import-Module Microsoft.Graph.Teams Connect-MgGraph -Scope Group.ReadWrite.All Connect-MicrosoftTeams $ProgressPreference = 'SilentlyContinue' ######################### #Variable definition: $DefaultModelTeam = "Team template ID" $MembersFilePath = "C:\Users\t130218\Desktop\owlimport_365.csv" $OwnersFilePath = "C:\Users\t130218\Desktop\TeamOwners.csv" ######################### Function CreaTeam{ param( [Parameter(Position=0)] [string]$displayName, [Parameter(Position=1)] [string]$description ) begin{ $params = @{ partsToClone = "apps,tabs,settings,channels" displayName = $displayName description = $description mailNickname = $displayName #visibility = "public" } #Disable "Crea" button in order to avoid duplicate Teams creation $btnCrea.enabled=$false #Message output and waiting time countdown for allow new Tean creation finalization $lblMessaggio.text="Creazione Team in corso..." $teamId= $txtTemplate.text Copy-MgTeam -TeamId $teamId -BodyParameter $params $lblTeamId.text = "Attendere 20 secondi" Start-Sleep -Seconds 5 $lblTeamId.text = "Attendere 15 secondi" Start-Sleep -Seconds 5 $lblTeamId.text = "Attendere 10 secondi" Start-Sleep -Seconds 5 $lblTeamId.text = "Attendere 5 secondi" Start-Sleep -Seconds 5 #The Teamid of the team that was just created can only be discovered via Team name search $newTeam= Get-MgGroup | Where-Object {$_.DisplayName -like $displayName} $lblTeamId.text=$newTeam.Id #Get Team members from the CSV $TeamUsers = Import-Csv $MembersFilePath -delimiter ";" #Iterate through each row obtained from the CSV and add to Teams as a Team member $TeamUsers | ForEach-Object { Add-TeamUser -GroupId $newTeam.id -User $_.m365_email -Role Member Write-host "Added User:"$_.m365_email -f Green } #Get Team owners from the CSV $TeamOwners = Import-Csv $OwnersFilePath -delimiter ";" #Iterate through each row obtained from the CSV and add to Teams as a Team member $TeamOwners | ForEach-Object { Add-TeamUser -GroupId $newTeam.id -User $_.m365_email -Role Owner Write-host "Added Owner:"$_.m365_email -f Green } } } Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.Application]::EnableVisualStyles() $CorsoTeams = New-Object system.Windows.Forms.Form $CorsoTeams.ClientSize = New-Object System.Drawing.Point(1200,575) $CorsoTeams.text = "Corso Teams - Crea Struttura" $CorsoTeams.TopMost = $false $lblNomeCorso = New-Object system.Windows.Forms.Label $lblNomeCorso.text = "Nome del corso" $lblNomeCorso.AutoSize = $true $lblNomeCorso.width = 25 $lblNomeCorso.height = 10 $lblNomeCorso.location = New-Object System.Drawing.Point(40,79) $lblNomeCorso.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $btnCrea = New-Object system.Windows.Forms.Button $btnCrea.text = "Crea" $btnCrea.width = 150 $btnCrea.height = 67 $btnCrea.location = New-Object System.Drawing.Point(373,298) $btnCrea.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',16) $btnChiudi = New-Object system.Windows.Forms.Button $btnChiudi.text = "Chiudi" $btnChiudi.width = 150 $btnChiudi.height = 67 $btnChiudi.location = New-Object System.Drawing.Point(628,298) $btnChiudi.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',16) $lblDataCorso = New-Object system.Windows.Forms.Label $lblDataCorso.text = "Data del corso" $lblDataCorso.AutoSize = $true $lblDataCorso.width = 25 $lblDataCorso.height = 10 $lblDataCorso.location = New-Object System.Drawing.Point(39,143) $lblDataCorso.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $lblDescrizione = New-Object system.Windows.Forms.Label $lblDescrizione.text = "Descrizione (facoltativa)" $lblDescrizione.AutoSize = $true $lblDescrizione.width = 25 $lblDescrizione.height = 10 $lblDescrizione.location = New-Object System.Drawing.Point(39,210) $lblDescrizione.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $txtDataCorso = New-Object system.Windows.Forms.TextBox $txtDataCorso.multiline = $false $txtDataCorso.width = 150 $txtDataCorso.height = 40 $txtDataCorso.enabled = $true $txtDataCorso.location = New-Object System.Drawing.Point(370,134) $txtDataCorso.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',20) $txtNomeTeam = New-Object system.Windows.Forms.TextBox $txtNomeTeam.multiline = $false $txtNomeTeam.width = 405 $txtNomeTeam.height = 40 $txtNomeTeam.enabled = $true $txtNomeTeam.location = New-Object System.Drawing.Point(370,75) $txtNomeTeam.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',20) $txtDescrizione = New-Object system.Windows.Forms.TextBox $txtDescrizione.multiline = $false $txtDescrizione.width = 405 $txtDescrizione.height = 40 $txtDescrizione.enabled = $true $txtDescrizione.location = New-Object System.Drawing.Point(370,210) $txtDescrizione.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',20) $btnChiudi = New-Object system.Windows.Forms.Button $btnChiudi.text = "Chiudi" $btnChiudi.width = 150 $btnChiudi.height = 67 $btnChiudi.location = New-Object System.Drawing.Point(628,298) $btnChiudi.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',16) $lblMessaggio = New-Object system.Windows.Forms.Label $lblMessaggio.text = "INSERIRE I DATI" $lblMessaggio.AutoSize = $true $lblMessaggio.width = 25 $lblMessaggio.height = 10 $lblMessaggio.location = New-Object System.Drawing.Point(40,493) $lblMessaggio.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $lblTemplate = New-Object system.Windows.Forms.Label $lblTemplate.text = "Modello Team utilizzato:" $lblTemplate.AutoSize = $true $lblTemplate.width = 25 $lblTemplate.height = 10 $lblTemplate.location = New-Object System.Drawing.Point(40,400) $lblTemplate.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',8) $txtTemplate = New-Object system.Windows.Forms.TextBox $txtTemplate.multiline = $false $txtTemplate.width = 405 $txtTemplate.height = 40 $txtTemplate.enabled = $true $txtTemplate.text = $DefaultModelTeam $txtTemplate.location = New-Object System.Drawing.Point(370,400) $txtTemplate.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',14) $lblTeamId = New-Object system.Windows.Forms.Label $lblTeamId.text = "" $lblTeamId.AutoSize = $true $lblTeamId.width = 25 $lblTeamId.height = 10 $lblTeamId.location = New-Object System.Drawing.Point(540,493) $lblTeamId.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $CorsoTeams.controls.AddRange(@($lblNomeCorso,$btnCrea,$lblDataCorso,$txtDataCorso,$txtNomeTeam,$btnChiudi,$lblMessaggio,$lblDescrizione,$txtDescrizione, $lblTeamId,$lblTemplate,$txtTemplate )) $txtDataCorso.text=Get-Date -Format "dd/MM/yyyy" $btnCrea.Add_Click({ $NomeTeamCompleto=$txtNomeTeam.text+" - "+$txtDataCorso.text CreaTeam $NomeTeamCompleto $txtDescrizione.text $lblMessaggio.text= "Team creato - TeamId:" }) $btnChiudi.Add_Click({$CorsoTeams.Close()}) [void]$CorsoTeams.ShowDialog()146Views0likes6CommentsPowerShell - GraphAPI - OneDrive Shared files - Error
Hello everybody, for changing the UPN for some number of users I need to export all the shared files in OneDrive for all users in the tenant. I have found an excellent script by the MVP Vasil Michev on the GitHub (PowerShell/Graph_ODFB_shared_files.ps1 at master · michevnew/PowerShell · GitHub). The script uses Entra ID App Registration with API permissions, GraphAPI and Powershell. I needed to change the script because it didn't export all the files, just some of them and i wanted to have a .csv export. After I changed the script, I get all the shared files, but the scripts stops after exporting the files from about half of my users (stops by the letter m in the UPN). This is the error that I am getting (for all the user until m in UPN the results are ok): ConvertFrom-Json : Cannot bind argument to parameter 'InputObject' because it is null. At C:\scripts\OneDrive_revised3.ps1:255 char:30 + return $result.Content | ConvertFrom-Json Is it an issue with a GraphAPI limitation (to many requests) or something else? How can I resolve this? Can I use a list of some users (from a csv file) to export the data only for these users? Here is the moddified script (I deleted Entra ID secrets and IDs): [CmdletBinding()] #Make sure we can use -Verbose Param([switch]$ExpandFolders,[int]$depth) function processChildren { Param( #Graph User object [Parameter(Mandatory=$true)]$User, #URI for the drive [Parameter(Mandatory=$true)][string]$URI, #Use the ExpandFolders switch to specify whether to expand folders and include their items in the output. [switch]$ExpandFolders, #Use the Depth parameter to specify the folder depth for expansion/inclusion of items. [int]$depth) $URI = "$URI/children" $children = @() #fetch children, make sure to handle multiple pages do { $result = Invoke-GraphApiRequest -Uri "$URI" -Verbose:$VerbosePreference $URI = $result.'@odata.nextLink' #If we are getting multiple pages, add some delay to avoid throttling Start-Sleep -Milliseconds 500 $children += $result } while ($URI) if (!$children) { Write-Verbose "No items found for $($user.userPrincipalName), skipping..."; continue } #handle different children types $output = @() $cFolders = $children.value | ? {$_.Folder} $cFiles = $children.value | ? {$_.File} #doesnt return notebooks $cNotebooks = $children.value | ? {$_.package.type -eq "OneNote"} #Process Folders foreach ($folder in $cFolders) { $output += (processFolder -User $User -folder $folder -ExpandFolders:$ExpandFolders -depth $depth -Verbose:$VerbosePreference) } #Process Files foreach ($file in $cFiles) { if ($file.shared) { $output += (processFile -User $User -file $file -Verbose:$VerbosePreference) } } #Process Notebooks foreach ($notebook in $cNotebooks) { if ($notebook.shared) { $output += (processFile -User $User -file $notebook -Verbose:$VerbosePreference) } } return $output } function processFolder { Param( #Graph User object [Parameter(Mandatory=$true)]$User, #Folder object [Parameter(Mandatory=$true)]$folder, #Use the ExpandFolders switch to specify whether to expand folders and include their items in the output. [switch]$ExpandFolders, #Use the Depth parameter to specify the folder depth for expansion/inclusion of items. [int]$depth) #prepare the output object $fileinfo = New-Object psobject $fileinfo | Add-Member -MemberType NoteProperty -Name "OneDriveOwner" -Value $user.userPrincipalName $fileinfo | Add-Member -MemberType NoteProperty -Name "Name" -Value $folder.name $fileinfo | Add-Member -MemberType NoteProperty -Name "ItemType" -Value "Folder" $fileinfo | Add-Member -MemberType NoteProperty -Name "Shared" -Value (&{If($folder.shared) {"Yes"} Else {"No"}}) #if the Shared property is set, fetch permissions if ($folder.shared) { $permlist = getPermissions $user.id $folder.id -Verbose:$VerbosePreference #Match user entries against the list of domains in the tenant to populate the ExternallyShared value $regexmatches = $permlist | % {if ($_ -match "\(?\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\)?") {$Matches[0]}} if ($permlist -match "anonymous") { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "Yes" } else { if (!$domains) { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "No domain info" } elseif ($regexmatches -notmatch ($domains -join "|")) { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "Yes" } else { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "No" } } $fileinfo | Add-Member -MemberType NoteProperty -Name "Permissions" -Value ($permlist -join ",") } $fileinfo | Add-Member -MemberType NoteProperty -Name "ItemPath" -Value $folder.webUrl #Since this is a folder item, check for any children, depending on the script parameters if (($folder.folder.childCount -gt 0) -and $ExpandFolders -and ((3 - $folder.parentReference.path.Split("/").Count + $depth) -gt 0)) { Write-Verbose "Folder $($folder.Name) has child items" $uri = "https://graph.microsoft.com/v1.0/users/$($user.id)/drive/items/$($folder.id)" $folderItems = processChildren -User $user -URI $uri -ExpandFolders:$ExpandFolders -depth $depth -Verbose:$VerbosePreference } #handle the output if ($folderItems) { $f = @(); $f += $fileinfo; $f += $folderItems; return $f } else { return $fileinfo } } function processFile { Param( #Graph User object [Parameter(Mandatory=$true)]$User, #File object [Parameter(Mandatory=$true)]$file) #prepare the output object $fileinfo = New-Object psobject $fileinfo | Add-Member -MemberType NoteProperty -Name "OneDriveOwner" -Value $user.userPrincipalName $fileinfo | Add-Member -MemberType NoteProperty -Name "Name" -Value $file.name $fileinfo | Add-Member -MemberType NoteProperty -Name "ItemType" -Value (&{If($file.package.Type -eq "OneNote") {"Notebook"} Else {"File"}}) $fileinfo | Add-Member -MemberType NoteProperty -Name "Shared" -Value (&{If($file.shared) {"Yes"} Else {"No"}}) #if the Shared property is set, fetch permissions if ($file.shared) { $permlist = getPermissions $user.id $file.id -Verbose:$VerbosePreference #Match user entries against the list of domains in the tenant to populate the ExternallyShared value $regexmatches = $permlist | % {if ($_ -match "\(?\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\)?") {$Matches[0]}} if ($permlist -match "anonymous") { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "Yes" } else { if (!$domains) { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "No domain info" } elseif ($regexmatches -notmatch ($domains -join "|")) { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "Yes" } else { $fileinfo | Add-Member -MemberType NoteProperty -Name "ExternallyShared" -Value "No" } } $fileinfo | Add-Member -MemberType NoteProperty -Name "Permissions" -Value ($permlist -join ",") } $fileinfo | Add-Member -MemberType NoteProperty -Name "ItemPath" -Value $file.webUrl #handle the output return $fileinfo } function getPermissions { Param( #Use the UserId parameter to provide an unique identifier for the user object. [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$UserId, #Use the ItemId parameter to provide an unique identifier for the item object. [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ItemId) #fetch permissions for the given item $uri = "https://graph.microsoft.com/beta/users/$($UserId)/drive/items/$($ItemId)/permissions" $permissions = (Invoke-GraphApiRequest -Uri $uri -Verbose:$VerbosePreference).Value #build the permissions string $permlist = @() foreach ($entry in $permissions) { #Sharing link if ($entry.link) { $strPermissions = $($entry.link.type) + ":" + $($entry.link.scope) if ($entry.grantedToIdentitiesV2) { $strPermissions = $strPermissions + " (" + (((&{If($entry.grantedToIdentitiesV2.siteUser.email) {$entry.grantedToIdentitiesV2.siteUser.email} else {$entry.grantedToIdentitiesV2.User.email}}) | select -Unique) -join ",") + ")" } if ($entry.hasPassword) { $strPermissions = $strPermissions + "[PasswordProtected]" } if ($entry.link.preventsDownload) { $strPermissions = $strPermissions + "[BlockDownloads]" } if ($entry.expirationDateTime) { $strPermissions = $strPermissions + " (Expires on: $($entry.expirationDateTime))" } $permlist += $strPermissions } #Invitation elseif ($entry.invitation) { $permlist += $($entry.roles) + ":" + $($entry.invitation.email) } #Direct permissions elseif ($entry.roles) { if ($entry.grantedToV2.siteUser.Email) { $roleentry = $entry.grantedToV2.siteUser.Email } elseif ($entry.grantedToV2.User.Email) { $roleentry = $entry.grantedToV2.User.Email } #else { $roleentry = $entry.grantedToV2.siteUser.DisplayName } else { $roleentry = $entry.grantedToV2.siteUser.loginName } #user claim $permlist += $($entry.Roles) + ':' + $roleentry #apparently the email property can be empty... } #Inherited permissions elseif ($entry.inheritedFrom) { $permlist += "[Inherited from: $($entry.inheritedFrom.path)]" } #Should have a Roles facet, thus covered above #some other permissions? else { Write-Verbose "Permission $entry not covered by the script!"; $permlist += $entry } } #handle the output return $permlist } function Renew-Token { #prepare the request $url = 'https://login.microsoftonline.com/' + $tenantId + '/oauth2/v2.0/token' $Scopes = New-Object System.Collections.Generic.List[string] $Scope = "https://graph.microsoft.com/.default" $Scopes.Add($Scope) $body = @{ grant_type = "client_credentials" client_id = $appID client_secret = $client_secret scope = $Scopes } try { Set-Variable -Name authenticationResult -Scope Global -Value (Invoke-WebRequest -Method Post -Uri $url -Debug -Verbose -Body $body -ErrorAction Stop) $token = ($authenticationResult.Content | ConvertFrom-Json).access_token } catch { $_; return } if (!$token) { Write-Host "Failed to aquire token!"; return } else { Write-Verbose "Successfully acquired Access Token" #Use the access token to set the authentication header Set-Variable -Name authHeader -Scope Global -Value @{'Authorization'="Bearer $token";'Content-Type'='application\json'} } } function Invoke-GraphApiRequest { param( [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$Uri ) if (!$AuthHeader) { Write-Verbose "No access token found, aborting..."; throw } try { $result = Invoke-WebRequest -Headers $AuthHeader -Uri $uri -Verbose:$VerbosePreference -ErrorAction Stop } catch [System.Net.WebException] { if ($_.Exception.Response -eq $null) { throw } #Get the full error response $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream()) $streamReader.BaseStream.Position = 0 $errResp = $streamReader.ReadToEnd() | ConvertFrom-Json $streamReader.Close() if ($errResp.error.code -match "ResourceNotFound|Request_ResourceNotFound") { Write-Verbose "Resource $uri not found, skipping..."; return } #404, continue #also handle 429, throttled (Too many requests) elseif ($errResp.error.code -eq "BadRequest") { return } #400, we should terminate... but stupid Graph sometimes returns 400 instead of 404 elseif ($errResp.error.code -eq "Forbidden") { Write-Verbose "Insufficient permissions to run the Graph API call, aborting..."; throw } #403, terminate elseif ($errResp.error.code -eq "InvalidAuthenticationToken") { if ($errResp.error.message -eq "Access token has expired.") { #renew token, continue Write-Verbose "Access token has expired, trying to renew..." Renew-Token if (!$AuthHeader) { Write-Verbose "Failed to renew token, aborting..."; throw } #Token is renewed, retry the query $result = Invoke-GraphApiRequest -Uri $uri -Verbose:$VerbosePreference } } else { Write-Verbose "Unexpected error: $errResp"; return } } #handle the output return $result.Content | ConvertFrom-Json } #Main script body #Make sure to define all the required variables $tenantId = "" $appID = "" $client_secret = "" #Use the ExpandFolders switch to specify whether to expand folders and include their items in the output. $ExpandFolders = $true #Use the Depth parameter to specify the folder depth for expansion/inclusion of items. $depth = 5 #Define the list of domains in the tenant $domains = @("contoso.com", "example.com") #Renew the token to ensure it's valid Renew-Token #Get all users in the tenant $users = Invoke-GraphApiRequest -Uri "https://graph.microsoft.com/v1.0/users?`$top=999" -Verbose:$VerbosePreference #Process each user foreach ($user in $users.value) { Write-Host "Processing $($user.userPrincipalName) OneDrive..." #Define the URI for the user's drive $uri = "https://graph.microsoft.com/v1.0/users/$($user.id)/drive/root" #Fetch items for the user's drive $items = processChildren -User $user -URI $uri -ExpandFolders:$ExpandFolders -depth $depth #Output the items for the user $sharedItems = $items | Where-Object { $_.Shared -eq "Yes" } if ($sharedItems.Count -gt 0) { $sharedItems | Export-Csv -Path "C:\temp\$($user.userPrincipalName)_OneDriveItems.csv" -NoTypeInformation -Force } } Write-Host "Export completed." Kindest regards, Leon Pavesic (LinkedIn) (Twitter)Solved1.1KViews0likes3CommentsUnable to Execute PowerShell Script Commands in Microsoft Teams Session Established via Script
I encountered an issue while attempting to execute PowerShell script commands within a Microsoft Teams session established via a script. The script includes commands to connect to Microsoft Teams using the Connect-MicrosoftTeams cmdlet and subsequently execute other Teams-related commands. While the script executes without errors, the session does not seem to be fully established, resulting in the following error when attempting to execute subsequent commands: powershell : Get-CsTeamsClientConfiguration : Session is not established, run Connect-MicrosoftTeams before requesting access token At line:1 char:1 + powershell -File 'C:\Users\***********************\script.p ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (Get-CsTeamsClie...ng access token:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError At C:\Users\****************************\script.ps1:8 char:1 + Get-CsTeamsClientConfiguration + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Get-CsTeamsClientConfiguration], UnauthorizedAccessException + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.Teams.ConfigApi.Cmdlets.GetCsTeamsClientCon figuration This issue is inconsistent, as the commands execute successfully when executed manually. Additionally, I've attempted to introduce delays in the script to allow for the session to fully establish, but the issue persists. This issue impacts the ability to automate tasks in Microsoft Teams using PowerShell scripts. Please let me know if there are any additional steps or information needed to address this issue effectively.1.3KViews0likes2CommentsTeams SIP Gateway Bulk Sign In Powershell
This article describes using PowerShell to initiate a Bulk Sign In of SIP Devices- https://learn.microsoft.com/en-us/microsoftteams/sip-gateway-configure NewCsSdgBulkSignInRequest -DeviceDetailsFilePath .\Example.csv -Region APAC However, I don't see NewCsSdgBulkSignInRequest as an available command in the PowerShell MicrosoftTeams 5.6.0 module. I even tried with the 5.6.1 preview module but that command does not exist when running "Get-Command -Module MicrosoftTeams". Is the command wrong or is there another module dependency not mentioned correctly in the article?Solved2.8KViews0likes4CommentsMS Teams Get-CsCallQueue Statistics field is blank again...
We has been using the Get-CsCallQueue cmdlet that comes in the Teams module to get the Number of Calls In Queue statistic for our call queues. As of a few days ago, this field has been coming back blank for all our queues. Does anyone know what causes this? A search of the web shows other people have had this issue over the last few years but I don't know if there is a solution on our end or if it is some change in Azure that is causing it.692Views2likes1CommentA PowerShell GUI for logging into Multiple Office365 Tenants and Services
Do you administer multiple Office365 tenants? what about multiple Office365 services? I'm a UC engineer and with Teams back ending into so many services these days its important that we as Teams admins have the ability to log into multiple office 365 PowerShell sessions. Personally I do a lot of Skype Online Shell and Exchange Online shell for multiple customers. So I wrote a free PowerShell tool that will save and login to multiple services and tenants simply and easily. It's called BounShell. Named after the BounSky, a tool some UC admins may be familiar with from the Lync/Skype days. It's free to download, free to use and completely open source and it's on the PowerShell gallery! You can install it right now from the PowerShell gallery by just running "Install-Module BounShell" but I'd highly recommend visiting www.ucmadscientist.com/BounShell and reading up on how to use it. (Oh, and you dont have to use the ISE) Any questions, ask below.4.4KViews0likes2CommentsMicrosoftTeams cmdlets not working with AccessToken
I am trying to run cmdlets from powershell module MicrosoftTeams (version 2.0.0) in a C# web application. I am using Authorization code flow and code from the answer provided in this post to acquire token: https://stackoverflow.com/questions/43488511/acquire-aad-token-using-asp-net-web-forms. Note: I had changed resource in the code to graph.windows.net to acquire AAD token. Token is acquired by using AuthenticationContext.AcquireTokenByAuthorizationCodeAsync method. Once the token is acquired, I run the following lines to create a powershell instance in C# and to import MicrosoftTeams Module. PowerShell pshell InitialSessionState iss; iss = InitialSessionState.CreateDefault2(); iss.ImportPSModule(new[] { "MicrosoftTeams" }); pshell = PowerShell.Create(iss); Then to connect with MicrosoftTeams, I run the following code: var connectCmd = new Command("Connect-MicrosoftTeams"); connectCmd.Parameters.Add("AadAccessToken", AccessToken); connectCmd.Parameters.Add("AccountId", "xxxxxxx@xxxxxx.onmicrosoft.com"); pshell.Commands.AddCommand(connectCmd); var result1 = pshell.Invoke(); Code works fine till here. After this I clear the shell commands and invoke the Get-CsTeamsCallingPolicy cmdlet: pshell.Commands.Clear(); pshell.Streams.Error.Clear(); pshell.AddScript("Get-CsTeamsCallingPolicy"); var result2 = pshell.Invoke(); After Invoke, I get an exception and this dialog pops up: Pressing 'Continue' brings back the same dialogue a couple of times. Exception details from this screen are: System.Collections.Generic.KeyNotFoundException was unhandled by user code HResult=-2146232969 Message=The given key was not present in the dictionary. Source=mscorlib StackTrace: at System.Collections.Concurrent.ConcurrentDictionary`2.get_Item(TKey key) at Microsoft.TeamsCmdlets.Powershell.Connect.Models.AzureSessionProvider.GetAccessToken(String resource, IEnumerable`1 scopes) in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\Models\AzureSession.cs:line 80 at Microsoft.TeamsCmdlets.Powershell.Connect.TeamsPowerShellSession.GetAccessToken(String resource, IEnumerable`1 scopes) in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\TeamsPowerShellSession.cs:line 82 at Microsoft.TeamsCmdlets.PowerShell.Connect.GetCsInternalAccessToken.ProcessRecord() in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\GetCsInternalAccessToken.cs:line 61 at System.Management.Automation.CommandProcessor.ProcessRecord() After pressing continue for the 3rd time, control goes back to C# code, and I receive the following runtime exception: Exception calling "GetSteppablePipeline" with "1" argument(s): "Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s): "Run either Connect-MicrosoftTeams or new-csonlinesession before running cmdlets."" Trying to run this logic from the powershell editor shows similar behavior: Running the following two lines: $AccessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' Connect-MicrosoftTeams -AadAccessToken $AccessToken -AccountId 'xxxxxxx@xxxxxx.onmicrosoft.com' gives this result: Account Environment Tenant TenantId ------- ----------- ------ -------- xxxxxxx@xxxxxx.onmicrosoft.com AzureCloud xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx I then run Get-Team cmdlet: Get-Team -User xxxxxxx@xxxxxxx.onmicrosoft.com which results in this message: Get-Team : The given key was not present in the dictionary. At line:1 char:1 + Get-Team -User xxxxxxx@xxxxxxx.onmicrosoft.com + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Get-Team], KeyNotFoundException + FullyQualifiedErrorId : System.Collections.Generic.KeyNotFoundException,Microsoft.TeamsCmdlets.PowerShell.Custom.GetTeam Running cmdlet Get-CsTeamsCallingPolicy yields this: Exception calling "GetSteppablePipeline" with "1" argument(s): "Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s): "Run either Connect-MicrosoftTeams or new-csonlinesession before running cmdlets."" At C:\Program Files\WindowsPowerShell\Modules\MicrosoftTeams\2.0.0\net472\SfBORemotePowershellModule.psm1:11369 char:13 + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myI ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : CmdletInvocationException If I run Connect-MicrosoftTeams directly from powershell without providing access token and accountid, I get the login screens and after login everything works fine i.e. I can run Get-Team and Get-CsTeamsCallingPolicy cmdlets successfully but I don't get this behavior when working with AadAccessToken. All the above code works fine if connecting to AzureAD module via Connect-AzureAD cmdlet like this both in web application and powershell editor: Connect-AzureAD -AadAccessToken $AccessToken -AccountId 'xxxxxxx@xxxxxxx.onmicrosoft.com' If someone has faced and successfully resolved this issue or have some tips on how to resolve this, please help. I have already tried a lot of things including searching for the specific exception messages and any possible solutions but found nothing that could help in this particular scenario, installed the latest version of MSTeams module, the previous version was old and did not have all the cmdlets that I am looking to work with. I installed the preview version of MSTeams module also to see if this issue is fixed in the upcoming release. Uninstalled the deprecated SkypeForBuisnessOnline Connector module, updated windows and so on. If you look at https://docs.microsoft.com/en-us/powershell/module/teams/connect-microsoftteams?view=teams-ps#example-4, this is what I am trying to achieve.2.1KViews1like5Comments