Help with creating powershell array with users from a CSV

Hi, We are doing a SfB to Teams Phone migration, migrating users in stages.

I’ve found a script that produces a report for Teams Policies assigned to users but it runs it against the whole tenant, includes filters to create the initial array but I want to run it against a group of users in a CSV. Can anybody tell me how I modify the script so it only runs against users listed in a csv?

Below is a portion of the script, any help is greatly appreciated.

$ModulesLoaded = Get-Module | Select-Object Name
If (!($ModulesLoaded -match "MicrosoftTeams")) {Write-Host "Please connect to the Microsoft Teams module and then restart the script"; break}
If (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online management module and then restart the script"; break}
[array]$Users = Get-CsOnlineUser -ResultSize 5000
# Filter the set to get Teams users - this will filter out all but cloud-only Teams users. If you don't want to use the filter, comment it out.
$Users = $Users | Where-Object {$_.InterpretedUserType -eq "PureOnlineTeamsOnlyUser" -or $_.InterpretedUserType -eq "PureOnlineTeamsOnlyUserFailedPublishingToAAD"} | Sort-Object DisplayName
If (!($Users)) {Write-Host "No users found - exiting"; break }
$Report = [System.Collections.Generic.List[Object]]::new()
# Process each user to fetch their policy assignments
ForEach ($User in $Users) {
    $TenantDefaultString = "Tenant Default" 
    $TeamsMeetingPolicy = $TenantDefaultString
    $TeamsMessagingPolicy = $TenantDefaultString
    $TeamsAppSetupPolicy = $TenantDefaultString
    $TeamsAppPermissionsPolicy = $TenantDefaultString
    $TeamsEncryptionPolicy = $TenantDefaultString
    $TeamsUpdatePolicy = $TenantDefaultSt

G’day and Welcome to the forum. :wave:t4:

Before we proceed … please go back, edit your question once again and fix the formatting of your code.

When you post code, sample data, console output or error messages please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.

Thanks in advance

How to format code in <---- Click :point_up_2:t4: :wink:

Regardless of that …

If you find some code online and you have an issue with it or questions about it you should try to reach the author of that code first and ask her or him for help. She or he will be the one most familiar with the code. :wink:

Thanks for welcoming me Olaf :+1:
I’ve updated my post with the correct format for the code.
I’ve reached out to the author but my task is a little time critical unfortunately so was hoping to ask the forum.

Your code seems incomplete though.

There are several issues with this code … let’s try to fix some of them …

A better option would be to use the #requires statement for the needed modules

Do you have more than 5000 users to process? If not - now that you have all users you may use your CSV data to compare against the list of all users and pick the ones you need. :man_shrugging:t4:

This line of code should be moved up one line.

This code does not make any sense because it does not use the loop variable $User. :man_shrugging:t4:

Hi Olaf, thanks for looking at this. I did only give a snippet of the code but here is the complete script

# ReportTeamsPolicyAssignments.PS1
# Generate a report about major Teams policies assigned to user accounts
$ModulesLoaded = Get-Module | Select-Object Name
If (!($ModulesLoaded -match "MicrosoftTeams")) {Write-Host "Please connect to the Microsoft Teams module and then restart the script"; break}
If (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online management module and then restart the script"; break}
[array]$Users = Get-CsOnlineUser -ResultSize 5000
# Filter the set to get Teams users - this will filter out all but cloud-only Teams users. If you don't want to use the filter, comment it out.
$Users = $Users | Where-Object {$_.InterpretedUserType -eq "PureOnlineTeamsOnlyUser" -or $_.InterpretedUserType -eq "PureOnlineTeamsOnlyUserFailedPublishingToAAD"} | Sort-Object DisplayName
If (!($Users)) {Write-Host "No users found - exiting"; break }
$Report = [System.Collections.Generic.List[Object]]::new()
# Process each user to fetch their policy assignments
ForEach ($User in $Users) {
    $TenantDefaultString = "Tenant Default" 
    $TeamsMeetingPolicy = $TenantDefaultString
    $TeamsMessagingPolicy = $TenantDefaultString
    $TeamsAppSetupPolicy = $TenantDefaultString
    $TeamsAppPermissionsPolicy = $TenantDefaultString
    $TeamsEncryptionPolicy = $TenantDefaultString
    $TeamsUpdatePolicy = $TenantDefaultString
    $TeamsChannelsPolicy = $TenantDefaultString
    $TeamsFeedbackPolicy = $TenantDefaultString
    $TeamsLiveEventsPolicy = $TenantDefaultString
    If ($User.TeamsMeetingPolicy) {$TeamsMeetingPolicy = $User.TeamsMeetingPolicy}
    If ($User.TeamsMessagingPolicy) {$TeamsMessagingPolicy = $User.TeamsMessagingPolicy}
    If ($User.TeamsAppSetupPolicy) {$TeamsAppSetupPolicy = $User.TeamsAppSetupPolicy}
    If ($User.TeamsAppPermissionPolicy) {$TeamsAppPermissionsPolicy = $User.TeamsAppPermissionPolicy}
    If ($User.TeamsEnhancedEncryptionPolicy) {$TeamsEncryptionPolicy = $User.TeamsEnhancedEncryptionPolicy}
    If ($User.TeamsUpdateManagementPolicy) {$TeamsUpdatePolicy = $User.TeamsUpdateManagementPolicy}
    If ($User.TeamsChannelsPolicy) {$TeamsChannelsPolicy = $User.TeamsChannelsPolicy}
    If ($User.TeamsFeedbackPolicy) {$TeamsFeedbackPolicy = $User.TeamsFeedbackPolicy}
    If ($User.TeamsMeetingBroadcastPolicy) {$TeamsLiveEventsPolicy = $User.TeamsMeetingBroadcastPolicy}
    # Output a report line
    $ReportLine = [PSCustomObject][Ordered]@{  
        User                         = $User.DisplayName
        UPN                          = $User.UserPrincipalName
        "Messaging Policy"           = $TeamsMessagingPolicy
        "Meeting Policy"             = $TeamsMeetingPolicy
        "App Setup Policy"           = $TeamsAppSetupPolicy
        "App Permissions Policy"     = $TeamsAppPermissionsPolicy
        "Enhanced Encryption Policy" = $TeamsEncryptionPolicy
        "Update Policy"              = $TeamsUpdatePolicy
        "Channels Policy"            = $TeamsChannelsPolicy
        "Feedback Policy"            = $TeamsFeedbackPolicy
        "Live Events"                = $TeamsLiveEventsPolicy
	"InterpretedUserType"        = $User.InterpretedUserType
 #  Add it to the report
$CSVOutput = "c:\temp\TeamsPolicyAssignments.CSV"
$ReportFile = "c:\temp\TeamsPolicyAssignments.html"
# Create the HTML report
$OrgDisplayName = (Get-OrganizationConfig).DisplayName
$CreationDate = Get-Date -format g
$Version = "1.0"
	   BODY{font-family: Arial; font-size: 8pt;}
	   H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}
	   TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}
	   TD{border: 1px solid #969595; padding: 5px; }
	   td.pass{background: #B7EB83;}
	   td.warn{background: #FFF275;}{background: #FF2626; color: #ffffff;}{background: #85D4FF;}
           <div align=center>
           <p><h1>Teams Policy Assignment Report</h1></p>
           <p><h2><b>For the " + $OrgDisplayName + " organization</b></h2></p>
           <p><h3>Generated: " + (Get-Date -format g) + "</h3></p></div>"

$htmlbody1 = $Report | ConvertTo-Html -Fragment
$htmltail = "<p>Report created for: " + $OrgDisplayName + "</p>" +
             "<p>Created: " + $CreationDate + "<p>" +
             "<p>Number of Teams users found:    " + $Users.Count + "</p>" +
             "<p>Teams Policy Assignment Report<b> " + $Version + "</b>"	
# Generate the HTML file
$htmlreport = $htmlhead + $htmlbody1 + $htmltail
$htmlreport | Out-File $ReportFile  -Encoding UTF8
Write-Host ("All done. Teams policies for {0} users analyzed. CSV file is available at {1} and a HTML report at {2}" -f $Users.Count, $CSVOutput, $ReportFile)
$Report | Out-GridView
$Report | Export-CSV -NoTypeInformation $CSVOutput

# An example script used to illustrate a concept. More information about the topic can be found in the Office 365 for IT Pros eBook
# and/or a relevant article on or See our post about the Office 365 for IT Pros repository # for information about the scripts we write.

# Do not use our scripts in production until you are satisfied that the code meets the need of your organization. Never run any code downloaded from the Internet without
# first validating the code in a non-production environment.

That actually only changes the reason for my last comment.

Again - if you only want to run this code for some of the users you may filter the array you create with the variable $Users for the ones you need. :man_shrugging:t4:

Office 365 PowerShell Scripts from the IT Pros eBook (

The script, among others were written by the team from office 365 IT pros.

If you look at their docs regarding their scripts, you will see this warning. Which means you need to think about what you want and how to modify this script. Or if it makes more sense to write your own from scratch if you end up modifying too much.

These are not production-ready scripts. You’ll need to do some work to prepare them for use in your environment.

Generally speaking, filtering through the entire tenant is not actually a bad way to go. You will likely have to do that in some fashion to create you csv, why not learn to do that in a way that puts the results into an object such as an array, or hash table, custom object etc. ?

I suggest that realizing right now it might be faster to somewhat manually build a csv if you are on a timetable but toss it out to get you thinking about the future.

The default filter criteria for the script were not overly useful in my tenant and is an example of one area of the script that may need to be adjusted to an individual environment. To my knowledge and that of a contact at MS who supports Teams, there is no official list of interpreted user types that we can reference in the MS docs. There are a couple lists floating around the internet that people have compiled and made educated guesses as to what the various values mean. Use them cautiously.

You also mention this was to be used to help with a SfB to Teams voice migration. None of the policies in the script are related to anything dealing with a dial tone (PSTN calls) . For example, these 3 policies deal with voice calls. Just add a Get or Set in front of them depending on what you are wanting to do.


While you have not provided enough information to guide you in using a csv file, a starting point would be to change this line in the script.

[array]$Users = Get-CsOnlineUser -ResultSize 5000

to something like this. Be sure to read the docs on Import-Csv for full detail on options.

$users = Import-Csv -path c:\PathOfFile 

What we do not know is what you want in the csv file specifically. Just a list of UPNs? SIP addresses?

It is possible the script authors may offer some help, but your best option is to just start figuring it out on your own through google searches for what other people have done, reading PS docs and specific questions here.

How familiar are you with Team policy precedence? That is also a topic that will inform any changes you make to the script.

Hi All, I’m sorry for the very late replay, I caught covid and have been off work.
I agree that I didnt provide enough information in my earlier post.
We are migrating staff in batches of 500 at a time, once a week because they all have sip phones, plus meeting room devices so it’s a lot of work.
All I want to do is run a report against those staff that have been migrated for the week, I dont want to include the staff that we have previously migrated in the report.
So I have a CSV that contains 500 users, in the CSV it has SIP, SAM, LineURI and LocationID. We are using Operator Connect and all users are already in a Dial Plan and Calling Policy.
What I want is for this script to run against only those 500 users in the CSV.
The information that I want to get back is - SIPaddress, LineURI, LocationID, Dial Plan and Calling Policy.

Again, sincerely sorry for the delay in replying

We do have more than 5000 users but I could get all users and just do lookups between the 2 CSV’s and do it that way, was just hoping to get use the script but that should be an ok way of doing it

I know what you mean. But since there is no way of telling Get-CsOnlineUser to get only a certain subset of some particular users I think it is apropriate to get all of them and filter them later on. Otherwise you’d need to provide a filter creteria what reliably picks the users you’re after. Therefor you’d need to have added a filterable property in advance representing the migration batch the user was member of. :man_shrugging:t3:

The other approach would be to query each individual user separately what’s probably much more time consuming than querying them all at once.

I need to start doing a month of lunchtimes to learn powershell to understand the basics as I feel foolish :slightly_smiling_face:
I thought that since I have the list of users in a CSV there would be a way to do a get-csonlineuser against that list. I thought I could do something like
$csv = import-csv .\ListOf500Users.csv
[array]Users = get-csonlineuser -identity $csv.sipAddress with some sort of ‘for each user’ statement?

You may start with learning the syntax of the help.

   [[-Identity] <UserIdParameter>]

It indicates pretty clearly that the parameter -Identitiy only takes a single value.

In contrast to the -Path parameter of the cmdlet Get-ChildItem for example …

   [[-Path] <string[]>]

… where you can pass more than one path at a time - shown by the empty [] square brackets.

I think I understand what you are going for, however do clarify if I get anything wrong.

You are migrating users from SfB to Teams
Each week you migrate 500, which is provided in a csv file.
You want to run a report on the recently migrated looking to verify specific attributes.

You probably already know this, but post migration before running any reports on migrated users you should give everything about 24 hours to be sure Teams processed the migration completely.

[array]Users = get-csonlineuser -identity $csv.sipAddress

If sipAddress is a column in the migration csv, that identifies a user to pull data from Teams for your post migration script/report. There is your “filter”.

For this

$csv = import-csv .\ListOf500Users.csv
[array]Users = get-csonlineuser -identity $csv.sipAddress

If you are going to try writing this on your own, vs using the other script you found, as a starting point you could do something along the lines of

$csv = import-csv -path .\ListOf500Users.csv 

     foreach($ID in $csv){
      get-csonlineuser -identity $ID.sipAddress  | select-object DisplayName,InterpretedUserType,TeamsUpgradeEffectiveMode,DialPlan,TeamsCallingPolicy,SIPaddress,LineURI      

There is a lot missing from above, but it might give you a starting point. What to do next and how to polish it depends on what you want to do with the result of get-csonlineuser.

There are also other parameters you can use with import-csv to ensure the data is usable.