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:IP_iPilotDomain -and !$iPilotDomain) {
            throw "Run Get-iPilotTeamsDomain or provide domain using -iPilotDomain"
        } elseif (!$iPilotDomain) {
            $iPilotDomain = $global:IP_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:IP_Instance) {
            $InitializeiPilotSessionSplat += @{
                Instance = $global:IP_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:IP_Instance) {
                    $Batch += @{
                        "instance"= $global:IP_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:IP_iPilotOAuthToken.access_token
                        "x-api-key"      = $global:IP_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
        }
    }
}