Public/New-iPilotTeamsBatchUserAssignment.ps1
Function New-iPilotTeamsBatchUserAssignment { <# .Synopsis Provisions users from a CSV file into iPilot .Description Provisions a batch of users into iPilot. CSV columns must be labeled (case sensitive): upn,firstName,lastName,telephoneNumber .Parameter CsvPath Path to the CSV file .Parameter ApiVersion Version of the NuWave iPilot to use. Defaults to v1. .Parameter iPilotDomain iPilot Domain Name. .Parameter Credential Credentials for iPilot API .Example # Provision all users from C:\user.csv into iPilot New-iPilotTeamsBatchUserAssignment -CsvPath C:\users.csv #> Param ( [ValidateScript({ if( -Not ($_ | Test-Path) ){ throw "File or folder does not exist" } return $true })] [System.IO.FileInfo] $CsvPath, [System.String] $ApiUrl = "https://api.nuwave.com", [System.String] $ApiKey, [System.String] $ApiVersion = "v1", [System.String] $iPilotDomain, [System.Management.Automation.PSCredential] $Credential, [Switch] $Verbose, [Switch] $Debug ) Begin { # UserPrincipalName validation If ($UserPrincipalName -and $UserPrincipalName -notlike "*@*.*") { throw "Check the formatting of the user's User Principal Name. Format should be jdoe@contoso.com" } # Set iPilot Domain if (!$global:iPilotDomain -and !$iPilotDomain) { throw "Run Get-iPilotTeamsDomain or provide domain using -iPilotDomain" } elseif (!$iPilotDomain) { $iPilotDomain = $global:iPilotDomain Write-Verbose "iPilot Domain: $iPilotDomain" } # else use passed in iPilotDomain } Process { # Verbose Switch if($PSBoundParameters.containskey("Verbose")) { $PreviousVerbosePreference = $VerbosePreference $VerbosePreference = "continue" } # Debug Switch if($PSBoundParameters.containskey("Debug")) { $PreviousDebugPreference = $DebugPreference $DebugPreference = "continue" } # Get/re-use OAuth Token $InitializeiPilotSessionSplat = @{ ApiUrl = $ApiUrl ApiVersion = $ApiVersion ApiKey = $ApiKey Credential = $Credential } if ($global:Instance) { $InitializeiPilotSessionSplat += @{ Instance = $global:Instance } } if ($VerbosePreference -eq "Continue") { $InitializeiPilotSessionSplat += @{ Verbose = $true } } if ($DebugPreference -eq "Continue") { $InitializeiPilotSessionSplat += @{ Debug = $true } } Initialize-iPilotSession @InitializeiPilotSessionSplat #region Get-Chunk - # https://github.com/mazzy-ax/Powershell.Chunks/tree/master/Powershell.Chunks # This project is released under the licensed under the MIT License. # Credit to: https://github.com/mazzy-ax function Get-Chunk { [OutputType("System.Array")] [CmdletBinding()] param ( [Parameter(Position = 0)] [int]$size = 1, [Parameter(ValueFromPipeline = $true)] $InputObject ) begin { $buf = @() } process { $buf += $InputObject if ( $size -gt 0 ) { # return chunks $start = 0 $end = $size - 1 $last = $buf.Count - 1 while ($end -le $last) { , $buf[$start..$end] $start = $end + 1 $end += $size } # store only items to be used, release unused elements for a Garbage Collection if ($start -gt $last) { $buf = @() } elseif ( $start -ne 0 ) { $buf = $buf[$start..$last] } } } end { if ( $size -lt 0 ) { # return a reverse ordered chunks $start = -1 $end = $size $last = - $buf.Count while ($end -ge $last) { , $buf[$start..$end] $start = $end - 1 $end += $size } if ($start -lt $last) { $buf = @() } elseif ( $start -ne -1 ) { $buf = $buf[$start..$last] } } if ( $buf ) { , $buf $buf = @() # release unused elements for a Garbage Collection } } } #endregion Get-Chunk # Build iPilot API Request $NewiPilotUserRequestUri = "$ApiUrl/$ApiVersion/msteams/$iPilotDomain/users" #region Build Request Body Write-Verbose "Request Uri: $NewiPilotUserRequestUri" Write-Verbose "Request Method: Post" # Import users from CSV $Users = Import-Csv $CsvPath $RowsMissingRequiredFields = $Users | Where-Object {!$_.upn -or !$_.telephoneNumber} if ($RowsMissingRequiredFields) { Write-Warning "Some rows are missing required upn or telephoneNumber:`n$($RowsMissingRequiredFields | Format-Table | Out-String)" Write-Warning "Press enter to continue without the rows above." pause $Users = $Users | Where-Object {$_.upn -and $_.telephoneNumber} } # Format $Users under user property $UsersObject = $Users | ForEach-Object { @{user = $_} } Write-Verbose "$($UsersObject.Count) entries imported from CSV" # Break $UsersObject into batches of 100 $Batches = $UsersObject | Get-Chunk 1-- -Verbose #endregion Build Request Body #region Submit Batch Requests # Iterate through each batch of 100 users Foreach ($Batch in $Batches) { # Specify Instance if ($global:Instance) { $Batch += @{ "instance"= $global:instance } } # Splat Invoke-RestMethod Parameters Write-Verbose "Batch:`n$($Batch | Format-Table | Out-String)" $NewiPilotUserInvokeParams = @{ Uri = $NewiPilotUserRequestUri Method = "Post" ContentType = "application/json" Headers = @{ "X-Access-Token" = $global:iPilotOAuthToken.access_token "x-api-key" = $global:iPilotApiKey } Body = $Batch | ConvertTo-Json } # Execute the REST API Try { $NewiPilotUserResponse = Invoke-RestMethod @NewiPilotUserInvokeParams -ErrorAction Stop } Catch { Write-Error "Failed to trigger provisioning:`n URL: $($NewiPilotUserRequestUri)`n Error: $($_.Exception.Message)`n Method: Post Headers: $($NewiPilotUserInvokeParams.Headers | Out-String) Body: $($Batch | ConvertTo-Json)" } # Return User object if ($NewiPilotUserResponse.statuscode -eq 200) { Write-Output "Successfully triggered provisioning for the below users:`n$($Batch | Format-Table | Out-String)" } else { Write-Error "Failed to trigger provisioning for the below users:`n$($Batch | Format-Table | Out-String)" } pause } #endregion Submit Batch Requests # Verbose Switch if($PSBoundParameters.containskey("Verbose")) { $VerbosePreference = $PreviousVerbosePreference } # Debug Switch if($PSBoundParameters.containskey("Debug")) { $DebugPreference = $PreviousDebugPreference } } } |