Public/Invoke-RealMigratorReport.ps1

Function Invoke-RealMigratorReport {

<#

        .SYNOPSIS
        
           Report uses API calls from Real Migrator to attain group member information in a GUI.

        .DESCRIPTION
          
            Using the GET API call, obtain group information from RealMigrator.
            Information included will be the user's assigned and the status of their PST migration.
            Also included will be information detailing the size and number of PSTs.
            This script creates a GUI to diplay the information.
            

        .INPUTS

            To connect to RealMigrator API, you need to be a Super Administrator for that instance of RealMigrator (NAM,HO,EU, etc.).
            Once a Super Administrator, copy the token from RealMigrator that you will paste into the GUI.

            Edit: 06/13/2019 You can share a Super Administrator's token if they give it to you. Keep this private.

            Then select what instance to view (North America, Europe, etc).
            Finally, select what group you would like to view.

        .OUTPUTS
         
            Use the export button to export the information displayed in the GUI to CSV file located in your OneDrive folder.
            Use the update button to update the RealMigrator Global Master Tracker.

        .NOTES

            Author: Jesse Newell
            Email: jesse.newell@dbschenker.com
            Last Edit: 2019-9-16
            Version 2.0
        
        #>


#Create hash table to share variables among runspace.
$Script:hash = [hashtable]::Synchronized(@{ })
$Script:Hash.runspaces = New-Object System.Collections.ArrayList
#Create a sessionstate function entry
$initialsessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
#Create runspacepool with a minumum of 1 runspace and a maximum of 40 runspaces running at a time.
$Script:Hash.runspacepool = [runspacefactory]::CreateRunspacePool(1, 40, $initialsessionstate, $Host)
$Script:Hash.runspacepool.ApartmentState = "STA"
$Script:Hash.runspacepool.ThreadOptions = "ReuseThread"
#Open pool
$Script:Hash.runspacepool.Open() 


#-------------------------------------------------------------------------------------------------#
#
# Form Section
#
#-------------------------------------------------------------------------------------------------#

$Script:Hash.SB_GUI = {

    Param ($hash)

    [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
    [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
 
    # Form class
    $Script:Hash.Form = New-Object system.Windows.Forms.Form
    $Script:Hash.Form.ClientSize = "940,600"
    $Script:Hash.Form.TopMost = $false
    $Script:Hash.Form.Text = "RealMigrator Report"
    $Script:Hash.Form.StartPosition = "CenterScreen"
    $Script:Hash.Form.CancelButton

    #Authorization label class
    $Script:Hash.Label = New-Object System.Windows.Forms.Label
    $Script:Hash.Label.Location = New-Object System.Drawing.Point(10, 20)
    $Script:Hash.Label.Size = New-Object System.Drawing.Size(250, 20)
    $Script:Hash.Label.Text = "Paste your authorization token below:"
    $Script:Hash.Form.Controls.Add($Script:Hash.Label)

    #Begin Label for Radio Button
    $Script:Hash.Label_radiobutton = New-Object system.windows.Forms.Label 
    $Script:Hash.Label_radiobutton.Text = "Update all clients on instance?"
    $Script:Hash.Label_radiobutton.AutoSize = $true
    $Script:Hash.Label_radiobutton.Width = 25
    $Script:Hash.Label_radiobutton.Height = 10
    $Script:Hash.Label_radiobutton.location = new-object system.drawing.point(280, 20)
    $Script:Hash.Label_radiobutton.Font = "Microsoft Sans Serif,10"
    $Script:Hash.Form.controls.Add($Script:Hash.Label_radiobutton)

    #radiobutton Yes
    $Script:Hash.radioButton = New-Object system.windows.Forms.RadioButton 
    $Script:Hash.radioButton.Text = "Yes"
    $Script:Hash.radioButton.AutoSize = $true
    $Script:Hash.radioButton.Width = 104
    $Script:Hash.radioButton.Height = 20
    $Script:Hash.radioButton.location = new-object system.drawing.point(280, 40)
    $Script:Hash.radioButton.Font = "Microsoft Sans Serif,10"
    $Script:Hash.Form.controls.Add($Script:Hash.radioButton)

    #radiobutton No
    $Script:Hash.radioButtonNo = New-Object system.windows.Forms.RadioButton 
    $Script:Hash.radioButtonNo.Text = "No"
    $Script:Hash.radioButtonNo.AutoSize = $true
    $Script:Hash.radioButtonNo.Width = 104
    $Script:Hash.radioButtonNo.Height = 20
    $Script:Hash.radioButtonNo.location = new-object system.drawing.point(360, 40)
    $Script:Hash.radioButtonNo.Font = "Microsoft Sans Serif,10"
    $Script:Hash.Form.controls.Add($Script:Hash.radioButtonNo)

    

    #Text box for pasting autorization token.
    $Script:Hash.TextBox = New-Object system.Windows.Forms.TextBox
    $Script:Hash.TextBox.multiline = $true
    $Script:Hash.TextBox.width = 250
    $Script:Hash.TextBox.height = 70
    $Script:Hash.TextBox.Location = New-Object System.Drawing.Point(10, 40)
    $Script:Hash.TextBox.Font = 'Microsoft Sans Serif,10'
    $Script:Hash.Form.Controls.Add($Script:Hash.TextBox)

    $Script:Hash.LabelComboBox = New-Object System.Windows.Forms.Label
    $Script:Hash.LabelComboBox.Location = New-Object System.Drawing.Point(500, 20)
    $Script:Hash.LabelComboBox.Size = New-Object System.Drawing.Size(200, 20)
    $Script:Hash.LabelComboBox.Text = 'Select a RealMigrator instance:'
    $Script:Hash.Form.Controls.Add($Script:Hash.LabelComboBox)

    # Label for groups
    $Script:Hash.LabelGroupInformation = New-Object System.Windows.Forms.Label
    $Script:Hash.LabelGroupInformation.Location = New-Object System.Drawing.Point(10, 120)
    $Script:Hash.LabelGroupInformation.size = New-Object System.Drawing.Size(600, 20)
    $Script:Hash.LabelGroupInformation.Text = $null
    $Script:Hash.LabelGroupInformation.Font = 'Microsoft Sans Serif,10'
    $Script:Hash.Form.controls.add($Script:Hash.LabelGroupInformation)

    #label for additional directions
    $Script:Hash.LabelInformation = New-Object System.Windows.Forms.Label
    $Script:Hash.LabelInformation.Location = New-Object System.Drawing.Point(10, 140)
    $Script:Hash.LabelInformation.size = New-Object System.Drawing.Size(450, 40)
    $Script:Hash.LabelInformation.Text = $null
    $Script:Hash.LabelInformation.Font = 'Microsoft Sans Serif,10'

    $Script:Hash.Form.Controls.Add($Script:Hash.LabelInformation)

    #label for duplicate updates
    $Script:Hash.LabelDuplicateInformation = New-Object System.Windows.Forms.Label
    $Script:Hash.LabelDuplicateInformation.Location = New-Object System.Drawing.Point(470, 140)
    $Script:Hash.LabelDuplicateInformation.size = New-Object System.Drawing.Size(450, 40)
    $Script:Hash.LabelDuplicateInformation.Text = $null
    $Script:Hash.LabelDuplicateInformation.Font = 'Microsoft Sans Serif,10'

    $Script:Hash.Form.Controls.Add($Script:Hash.LabelDuplicateInformation)


    #label for instance selection
    $Script:Hash.LabelPrefix = New-Object System.Windows.Forms.Label
    $Script:Hash.LabelPrefix.Location = New-Object System.Drawing.Point(700, 20)
    $Script:Hash.LabelPrefix.Size = New-Object System.Drawing.Size(450, 20)
    $Script:Hash.LabelPrefix.Text = "Select a group:"
    $Script:Hash.Form.Controls.Add($Script:Hash.LabelPrefix)

    # List populating the list information
    $Script:Hash.ListBoxClub = New-Object System.Windows.Forms.ComboBox 
    $Script:Hash.ListBoxClub.Location = New-Object System.Drawing.Size(700, 40) 
    $Script:Hash.ListBoxClub.Enabled = $false
    $Script:Hash.ListBoxClub.Size = New-Object System.Drawing.Size(220, 50) 
    $Script:Hash.ListBoxClub.DropDownHeight = 400
    $Script:Hash.Form.Controls.Add($Script:Hash.ListBoxClub)

    $Script:Hash.ArrayInstance = @("Head Office", "Germany", "Europe", "North America", "South America", "Asia Pacific", "China")
    $Script:Hash.ComboBoxInstance = New-Object System.Windows.Forms.ListBox 
    $Script:Hash.ComboBoxInstance.Location = New-Object System.Drawing.Size(500, 40) 
    $Script:Hash.ComboBoxInstance.Size = New-Object System.Drawing.Size(150, 20) 
    $Script:Hash.ComboBoxInstance.Height = 80 
    $Script:Hash.Form.Controls.Add($Script:Hash.ComboBoxInstance) 
    foreach ($Item in $Script:Hash.ArrayInstance) {
        [void]$Script:Hash.ComboBoxInstance.Items.Add($item)
    }

    $Script:Hash.ComboBoxInstance_SelectedIndexChanged = {

        
        
        $Script:Hash.PSRealMigratorComboInstance = [powershell]::Create().AddScript($Script:Hash.SB_InstanceSelection).AddArgument($Script:Hash)
        $Script:Hash.PSRealMigratorComboInstance.RunspacePool = $Script:Hash.runspacepool

        $Script:Hash.PSRealMigratorComboInstance.BeginInvoke()
 
        
    }

    $Script:Hash.ComboBoxInstance.add_SelectedIndexChanged($Script:Hash.ComboBoxInstance_SelectedIndexChanged)

    $Script:Hash.listBoxClub.add_SelectedIndexChanged( { 
   

            $Script:Hash.PSRealMigratorGroupList = [powershell]::Create().AddScript($Script:Hash.SB_GetMemberVariables).AddArgument($Script:Hash)
            $Script:Hash.PSRealMigratorGroupList.RunspacePool = $Script:Hash.runspacepool

            $Script:Hash.PSRealMigratorGroupList.BeginInvoke()
    
        })


    # Add a listview.
    $Script:Hash.Listview = New-Object System.Windows.Forms.ListView
    $Script:Hash.Listview.Location = New-Object System.Drawing.Size(10, 180)
    $Script:Hash.Listview.Size = New-Object System.Drawing.Size(915, 330)
    $Script:Hash.Listview.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor
    [System.Windows.Forms.AnchorStyles]::Right -bor
    [System.Windows.Forms.AnchorStyles]::Top -bor
    [System.Windows.Forms.AnchorStyles]::Left
    $Script:Hash.Listview.View = "Details"
    $Script:Hash.Listview.FullRowSelect = $true
    $Script:Hash.Listview.multiselect = $true
    $Script:Hash.Listview.AllowColumnReorder = $true
    $Script:Hash.Listview.GridLines = $true
    $Script:Hash.Form.Controls.Add($Script:Hash.Listview)


    # Add Update Report Button.
    $Script:Hash.button_updatereport = New-Object System.Windows.Forms.Button
    $Script:Hash.button_updatereport.Location = New-Object System.Drawing.Size(480, 530)
    $Script:Hash.button_updatereport.Size = New-Object System.Drawing.Size(240, 32)
    $Script:Hash.button_updatereport.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor
    [System.Windows.Forms.AnchorStyles]::Right
    $Script:Hash.button_updatereport.TextAlign = "MiddleCenter"
    $Script:Hash.button_updatereport.Text = "Update Report"
    $Script:Hash.button_updatereport.Enabled = $true
    $Script:Hash.button_updatereport.Add_click( {

        

            $Script:Hash.PSRealMigratorUpdateReport = [powershell]::Create().AddScript($Script:Hash.SB_UpdateReport).AddArgument($Script:Hash)
            $Script:Hash.PSRealMigratorUpdateReport.runspacepool = $Script:Hash.runspacepool

            $Script:Hash.PSRealMigratorUpdateReport.BeginInvoke()
                    

    
        } )
    $Script:Hash.form.controls.add($Script:Hash.button_updatereport)

    # Add Run Export Button.
    $Script:hash.button_runexport = New-Object System.Windows.Forms.Button
    $Script:hash.button_runexport.Location = New-Object System.Drawing.Size(200, 530)
    $Script:hash.button_runexport.Size = New-Object System.Drawing.Size(240, 32)
    $Script:hash.button_runexport.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor
    [System.Windows.Forms.AnchorStyles]::Right
    $Script:hash.button_runexport.TextAlign = "MiddleCenter"
    $Script:hash.button_runexport.Text = "Export Report"
    $Script:hash.button_runexport.Enabled = $true
    $Script:hash.button_runexport.Add_click( {


            $Script:Hash.PSRealMigratorExportReport = [powershell]::Create().AddScript($Script:Hash.SB_ExportReport).AddArgument($Script:Hash)
            $Script:Hash.PSRealMigratorExportReport.RunspacePool = $Script:Hash.runspacepool
            $Script:Hash.PSRealMigratorExportReport.BeginInvoke()
        }
    
    )
    $Script:Hash.form.controls.add($Script:Hash.button_runexport)
    
    $Script:Hash.Form.add_FormClosing( {
    
    
            #Clean up the rest of the runspaces when done
        
            if ($script:Hash.PSRealMigratorGetData -ne $null) {
                $script:Hash.PSRealMigratorGetData.Dispose()
            }
            if ($script:Hash.PSRealMigratorGroupList -ne $null) {
        
                $script:Hash.PSRealMigratorGroupList.Dispose()
            }
            if ($script:Hash.PSRealMigratorListView -ne $null) {
        
                $script:Hash.PSRealMigratorListView.Dispose()
            }
            if ($script:Hash.PSRealMigratorStartRunSpaces -ne $null) {
        
                $script:Hash.PSRealMigratorStartRunSpaces.Dispose()
            }   
            if ($script:Hash.PSRealMigratorComboInstance -ne $null) {
        
                $script:Hash.PSRealMigratorComboInstance.Dispose()
            }
            if ($script:Hash.PSRealMigratorCleanUpRunSpace -ne $null) {
         
                $script:Hash.PSRealMigratorCleanUpRunSpace.Dispose()
            }

            if ($script:Hash.PSRealMigratorUniques -ne $null) {
                    
                $script:Hash.PSRealMigratorUniques.Dispose()
            }
            if ($script:Hash.PSRealMigratorDuplicates -ne $null) {
                    
                $script:Hash.PSRealMigratorDuplicates.Dispose()
            }

            if ($script:Hash.runspaces -ne $null) {
         
                $script:Hash.runspaces.Clear()
         
            }
            
    
        })


    $Script:Hash.Form.ShowDialog()

}
#Form runspace creation
$Script:Hash.PSRealMigratorForm = [powershell]::Create().AddScript($Script:Hash.SB_GUI).AddArgument($Script:Hash)
$Script:Hash.PSRealMigratorForm.RunspacePool = $Script:Hash.runspacepool

$psdata = $Script:Hash.PSRealMigratorForm.BeginInvoke()


#-------------------------------------------------------------------------------------------------#
#
# Instance Selection Runspace
#
#-------------------------------------------------------------------------------------------------#


$Script:Hash.SB_InstanceSelection = {


    Param($hash)

    #Change the selected instance to something the API GET can understand.
    if ($Script:Hash.ComboBoxInstance.Text -eq 'Head Office') {  
        $Script:Hash.Instance = 'ho'
        $Script:Hash.Source = "https://dbschenker.sharepoint.com/sites/GlobalWorkplaceManagement/GlobalRollout/Shared%20Documents/PST%20Migrations/Country%20Rollout/HO(DE7499)%20Regional%20Tracker.xlsx?web=1"
    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'Germany') { 
        $Script:Hash.Instance = 'de'   
    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'Europe') {  
        $Script:Hash.Instance = 'eu' 
        $Script:Hash.Source = "https://dbschenker.sharepoint.com/sites/GlobalWorkplaceManagement/GlobalRollout/Shared%20Documents/PST%20Migrations/Country%20Rollout/EMEA%20Regional%20Tracker.xlsx?web=1"
    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'North America') {   
        $Script:Hash.Instance = 'us'  
        $Script:Hash.Source = "https://dbschenker.sharepoint.com/sites/GlobalWorkplaceManagement/GlobalRollout/Shared%20Documents/PST%20Migrations/Country%20Rollout/AMER%20Regional%20Tracker.xlsx?web=1"

    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'South America') {   
        $Script:Hash.Instance = 'sam' 
        $Script:Hash.Source = "https://dbschenker.sharepoint.com/sites/GlobalWorkplaceManagement/GlobalRollout/Shared%20Documents/PST%20Migrations/Country%20Rollout/AMER%20Regional%20Tracker.xlsx?web=1"   
    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'Asia Pacific') {
        $Script:Hash.Instance = 'apac'
        $Script:Hash.Source = "https://dbschenker.sharepoint.com/sites/GlobalWorkplaceManagement/GlobalRollout/Shared%20Documents/PST%20Migrations/Country%20Rollout/APAC%20Regional%20Tracker.xlsx?web=1"
    }
    if ($Script:Hash.ComboBoxInstance.Text -eq 'China') {
        $Script:Hash.Instance = 'cn'
    }
       
    #Restart listbox
    $Script:Hash.ListBoxClub.Items.Clear()
    #Set token variable
    $Script:Hash.Token = $Script:Hash.TextBox.text

        
    #If radiobutton Yes is checked to look for all clients
    if ($hash.radioButton.Checked) {
            
         
        if ($Script:Hash.PSRealMigratorStartRunSpaces -ne $null) {
            
            $Script:Hash.PSRealMigratorStartRunSpaces.Dispose()
            
        }

        #Disable the list box for individual group selection.
        $Script:Hash.ListBoxClub.Enabled = $false
            
        # Get all users from instance
        $Script:hash.members = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($Script:Hash.Token)" "https://schenker$($Script:Hash.Instance).realmigrator.com/api/admin/clients" --silent | ConvertFrom-Json -ErrorAction Stop
            
        #Check if the token threw an error
        if ($hash.members.message -eq "invalid or expired jwt" -or $hash.members.message -eq "Unauthorized") {
            #display error
            $Script:Hash.LabelInformation.ForeColor = "Red"
            $Script:Hash.LabelInformation.Text = "The token you attempted to use is invalid or expired. Please request a new token from a Super Administrator for the requested instance."
        }

        else {
               
            #Set user variables
            $Script:Hash.Users = $Script:Hash.Members | Select-Object objectID, UPN, DisplayName

            $Script:Hash.Usertotal = $Script:Hash.Users.Count

            $Script:Hash.LabelGroupInformation.ForeColor = "Black"
            $Script:Hash.LabelGroupInformation.Text = "$($Script:Hash.Usertotal) users in $($Script:Hash.ComboBoxInstance.Text) RealMigrator Instance. Please wait..."
            $Script:Hash.LabelInformation.Text = $Null

            #Start runspace for user object creation
            $Script:Hash.PSRealMigratorStartRunSpaces = [powershell]::Create().AddScript($Script:Hash.SB_StartRunSpaces).AddArgument($Script:Hash)
            $Script:Hash.PSRealMigratorStartRunSpaces.RunspacePool = $Script:Hash.runspacepool
            $Script:Hash.HandleStartRunSpaces = $Script:Hash.PSRealMigratorStartRunSpaces.BeginInvoke()

            # Start the get data runspace to pull the data from the runspace pool and clean up unused runspaces.
            $Script:Hash.PSRealMigratorGetData = [powershell]::Create().AddScript($Script:Hash.SB_GetData).AddArgument($Script:Hash)
            $Script:Hash.PSRealMigratorGetData.RunspacePool = $Script:Hash.runspacepool
            $Script:Hash.PSRealMigratorGetData.BeginInvoke()


        }

    }
    #If no button is checked, allow to get invidual group selection

    elseif ($hash.radiobuttonNo.Checked) {

        #Enable group section
        $Script:Hash.ListBoxClub.Enabled = $true

        #Obtain the groups of the selected instance.
        $script:Hash.Results = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($Script:Hash.Token)" "https://schenker$($Script:Hash.Instance).realmigrator.com/api/admin/groups" -s | 
        ConvertFrom-Json 
        if ($hash.members.message -eq "invalid or expired jwt" -or $hash.members.message -eq "Unauthorized") {
                    
            #Display error if token is incorrect
            $Script:Hash.LabelInformation.ForeColor = "Red"
            $Script:Hash.LabelInformation.Text = "The token you attempted to use is invalid or expired. Please request a new token from a Super Administrator for the requested instance."
            
        }

        else {
            
            #Allow the group selection
            $Script:Hash.LabelInformation.ForeColor = "Black"
            $Script:Hash.LabelInformation.Text = "Select a group"
            # Sort the objects for easy display
            $Script:Hash.Sorted = $script:Hash.Results | Sort-Object -Property DisplayName | select-Object DisplayName, ObjectID, Finalize

      
            #Add the displaynames to the List Box
            foreach ($grp in $Script:Hash.Sorted) {
                
             
                $Script:Hash.ListBoxClub.Items.Add($grp.displayname)

     
            }

        }

    }
                       

}


##-------------------------------------------------------------------------------------------------#
#
# Member and variable section for individual groups
#
##-------------------------------------------------------------------------------------------------#

$Script:Hash.SB_GetMemberVariables = {
 

    Param($hash)
        
    # Get objects based on selected group.
    $Script:Hash.Group = $Script:Hash.Results | Where-Object { $_.DisplayName -eq $Script:Hash.ListBoxClub.SelectedItem }

    #Variable for group object ID
    $Script:Hash.GroupObjectID = $Script:Hash.Group.ObjectID
    # Variable for group displayname
    $Script:Hash.GroupDisplayName = $Script:Hash.Group.DisplayName

    #Variable if the group has been set for finalization yet.
    $Script:Hash.GroupFinalize = $Script:Hash.Group.Finalize

        

    #Get group members based on group object ID
    $Script:Hash.Members = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($Script:Hash.Token)" "https://schenker$($Script:Hash.Instance).realmigrator.com/api/admin/groups/$($Script:Hash.GroupObjectID)/members" -s | 
    ConvertFrom-Json

    #If there are no members say so
    if (!$Script:Hash.Members) {
        Write-Verbose "No users assigned to $($Script:Hash.GroupDisplayName)"
        #Variable for the name of the CSV file to be exported
        $Script:Hash.LabelGroupInformation.Text = "No users assigned to $($Script:Hash.Group.DisplayName)."

    }

    # Get user information
    else {
        $Script:Hash.LabelInformation.Text = $Null                  
        $Script:Hash.Users = $Script:Hash.Members | Select-Object objectID, UPN, DisplayName
        #Get the number of users
        $Script:Hash.Usertotal = $Script:Hash.Users.Count
        #Variable for the name of the CSV file to be exported
        $Script:Hash.LabelGroupInformation.ForeColor = "Black"
        $Script:Hash.LabelGroupInformation.Text = "$($Script:Hash.Usertotal) users in $($Script:Hash.GroupDisplayName). Please wait..."
            
        #Start runspace creation for each individual user for parallel processing.
        $Script:Hash.PSRealMigratorStartRunSpaces = [powershell]::Create().AddScript($Script:Hash.SB_StartRunSpaces).AddArgument($Script:Hash)
        $Script:Hash.PSRealMigratorStartRunSpaces.RunspacePool = $Script:Hash.runspacepool
        $Script:Hash.HandleStartRunSpaces = $Script:Hash.PSRealMigratorStartRunSpaces.BeginInvoke()
       

        
        # Start the get data runspace to pull the data from the runspace pool and clean up unused runspaces.
        $Script:Hash.PSRealMigratorGetData = [powershell]::Create().AddScript($Script:Hash.SB_GetData).AddArgument($Script:Hash)
        $Script:Hash.PSRealMigratorGetData.RunspacePool = $Script:Hash.runspacepool
        $Script:Hash.PSRealMigratorGetData.BeginInvoke()
    

    }

 
}

#--------------------------------------------------------------------------------------------------------------------------#
#
# Parallel Processing: Create User Objects Section
#
#--------------------------------------------------------------------------------------------------------------------------#

     
$Script:Hash.SB_StartRunSpaces = {

    Param($hash)



    $Count = 0

    ForEach ($user in $Script:Hash.users) {
        #Create the powershell instance and supply the scriptblock with the other parameters
        $Script:Hash.powershell = [powershell]::Create().AddScript( {
            
                Param($user, $script:hash)
                $usercollection = @()
                


                $username = $user.UPN
                $object = $script:hash[$user.ObjectID] = ($user.ObjectID)
                $displayname = $script:hash[$user.DisplayName] = ($user.DisplayName)


                $upn = $script:hash[$user.UPN] = ([string]$username)

                $group = $script:hash[$user.ObjectID] = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($hash.token)" "https://schenker$($hash.instance).realmigrator.com/api/admin/clients/$object/group" -s | 
                ConvertFrom-Json

              

                $groupdisplayname = $group.DisplayName
                $groupfinalize = $group.Finalize

                if (!$groupdisplayname) {
                
                    
                    $groupdisplayname = "Unassigned"
                
                }


                $details = $hash[$user.ObjectID] = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($hash.token)" "https://schenker$($hash.instance).realmigrator.com/api/admin/module/pst/$object" -s | 
                ConvertFrom-Json

                $statusdetails = $hash[$user.ObjectID] = curl.exe -X GET --header "Accept: application/json" --header "Authorization: Bearer $($hash.token)" "https://schenker$($hash.instance).realmigrator.com/api/admin/clients/$object" -s | 
                ConvertFrom-Json


                #Set variable for RealMigrator message.
                $statussync = $statusdetails.status.message
                $statusstate = $statusdetails.status.state

                #Set variables for PST information
                $filessynced = $details.Statistics.filesSynced
                $filetotal = $details.Statistics.filesTotal
                $sizesynced = $details.Statistics.sizeSynced
                $sizetotal = $details.Statistics.sizeTotal
                

                #Convert size of the PST from bites to MBs (decimal)
                $mbtotal = [int]($sizetotal / 1000000)
                $mbsynced = [int]($sizesynced / 1000000)


                # Find out if the PST size is warranted for auto expansion.
                if ($mbtotal -gt 100000) {
                    $expand = 'TRUE'
                }
                else {
                    $expand = 'FALSE'
                }


                if ($mbtotal -gt 0) {
                    if ($statussync -eq 'Sleeping.' -or $statussync -eq 'running pst module.' -or $statussync -eq 'Finished all modules.') {

                        if ($filetotal) {

                            if ($filessynced -eq $filetotal) {
                                $progress = "IN SYNC"

                            }
                            if ($sizesynced -eq 0) {
                                $progress = "NOT STARTED"
                            }

                            if ($filessynced -ne $filetotal) {
                                $progress = "IN PROGRESS"
                            }
          

                        }

                    }

                    if ($statussync -eq 'Finished migration.') {


                        if ($statusstate -eq 1) {

                            $progress = "FINISHED"

                        }

                        if ($statusstate -eq 2) {

                            $progress = "FINISHED WITH ERRORS"

                        }


                        $syncedstatus = "$mbsynced` of $mbtotal`mb synced"
                        $filesyncstatus = "$filessynced` of $filetotal PSTs"
                    

                    }

                    $syncedstatus = "$mbsynced` of $mbtotal`mb synced"
                    $filesyncstatus = "$filessynced` of $filetotal PSTs"
                }

                if ($mbtotal -eq 0) {

                    $syncedstatus = $null
                    $filesyncstatus = $null
                    $progress = "No PST"
                    $statussync = "Not relevant"

                }

        
                #If there are PSTs, set their status.
                

                if ($groupfinalize) {

                    $finalizationdate = $groupfinalize

                }

                else {

                    $finalizationdate = $null

                }


                # Create properties for custom objects
                $properties = @{

                    UserPrincipalName = $upn   
                    DisplayName       = $displayname
                    ClientID          = $object
                    SizeInMB          = $mbtotal
                    AutoExpandArchive = $expand
                    PSTImportStatus   = $progress
                    SizeSyncStatus    = $syncedstatus
                    PSTSyncTotal      = $filesyncstatus
                    FinalizationDate  = $finalizationdate
                    Status            = $statussync
                    RMGroup           = $groupdisplayname
            

                    
                }

                $usercollection += New-Object PSObject -Property $properties | Select-object UserPrincipalName, RMGroup, FinalizationDate, DisplayName, ClientID, SizeInMB, AutoExpandArchive, Status, PSTImportStatus, SizeSyncStatus, PSTSyncTotal

                #pass results to the GetData scriptblock
                $usercollection

            
            }).AddArgument($user).AddArgument($script:hash)
           
        $Script:hash.powershell.RunspacePool = $script:hash.runspacepool
        #Create a temporary collection for each runspace
        $Script:hash.temp = "" | Select-Object PowerShell, Runspace, User
        $Script:hash.Temp.User = $user
        $Script:hash.temp.PowerShell = $Script:hash.powershell
        $Count++
            
        $Script:Hash.LabelGroupInformation.Text = "$($Count) runspaces created for parallel information gathering. Please wait..."

        #Save the handle output when calling BeginInvoke() that will be used later to end the runspace
        $Script:hash.temp.Runspace = $Script:hash.powershell.BeginInvoke()
    
        $Script:hash.runspaces.Add($Script:Hash.temp) | Out-Null   


    }
 
}


      
#-------------------------------------------------------------------------------------------------------------------#
#
# Display Results section
#
#-------------------------------------------------------------------------------------------------------------------#

$Script:hash.SB_GetListViewInformation = {

 
    Param($hash)
    
    $Script:Hash.LabelGroupInformation.Text = "Displaying user information."
    $Script:Hash.LabelInformation.Text = $Null
    
    #Clear the list view
    $Script:Hash.listview.Items.Clear()
    $Script:Hash.listview.Columns.Clear()

    #Populate information to the listview
    $Script:hash.properties = $script:hash.report[0].psObject.Properties               
    $Script:hash.properties | ForEach-Object {
        $Script:hash.listview.Columns.Add("$($_.Name)") | Out-Null
                         
    }
        
        

    foreach ($email in $Script:hash.report) {

        
        #Create the powershell instance and supply the scriptblock with the other parameters
        $Script:Hash.pslistview = [powershell]::Create().AddScript( {
            
                Param($email, $script:hash) 
                   
                $Username = New-Object System.Windows.Forms.ListViewItem($email.'UserPrincipalName')          
                $script:hash.properties | Where-Object { $_.Name -ne 'UserPrincipalName' } | ForEach-Object {
                    $columnName = "$($_.Name)"
                    # Add the custom item to the appropriate column.
                    $username.SubItems.add("$($email.$columnName)") | out-null              
                }
                # Finalize the listview item.
                $script:hash.listview.Items.Add($Username) | out-null  
            
            }).AddArgument($email).AddArgument($script:hash)
           
        $Script:Hash.pslistview.runspacepool = $Script:Hash.runspacepool
        #Create a temporary collection for each runspace
        $Script:Hash.temp = "" | Select-Object Powershell, Runspace, Email
        $Script:Hash.Temp.Email = $email
        $Script:Hash.temp.powershell = $script:Hash.pslistview
            
            
        #Save the handle output when calling BeginInvoke() that will be used later to end the runspace
        $Script:Hash.temp.Runspace = $Script:Hash.pslistview.BeginInvoke()
           

        $Script:Hash.runspaces.Add($Script:Hash.temp) | Out-Null   
            
    }

        
    # Resize the columns based on the size of the header.
    $Script:hash.listview.AutoResizeColumns("HeaderSize")
    $Script:Hash.LabelGroupInformation.ForeColor = "Green"
    if ($hash.radioButton.Checked) {
        
        $Script:Hash.LabelGroupInformation.Text = "$($Script:Hash.ComboBoxInstance.Text) RealMigrator Instance - All user information collected."
        
    }
    else {
        $Script:Hash.LabelGroupInformation.Text = "User PST information collected from $($hash.GroupDisplayName)"
    
    }
    
        

    $Script:Hash.Reporttoexport = @()
    $Script:hash.reporttoexport = $Script:hash.report

        
        
        
       
}


#------------------------------------------------------------------------------------------------------#
#
# Cleanup Unused Runspaces Section
#
#------------------------------------------------------------------------------------------------------#


$Script:Hash.SB_CleanUpRunspaces = {

    Param($hash)
    $Wait = $True

    Do {
        $more = $false  
        Foreach ($runspace in $Script:Hash.runspaces) { 
    
            If ($script:Hash.runspace.Runspace.isCompleted) {
                $runspace.pslistview.EndInvoke($runspace.Runspace)
                $runspace.pslistview.dispose()
                $runspace.Runspace = $null
                $runspace.pslistview = $null                 
            } 
            
            ElseIf ($runspace.Runspace -ne $null) {
                $more = $true
            }

        }
        $script:hash.runspaceCount = ($script:hash.Runspaces | Where-Object { $_.Runspace -ne $Null }).Count
        If ($more -AND $Wait) {
            Start-Sleep -Milliseconds 100
            
        }   
        #Clean out unused runspace jobs
        $script:hash.temphash = $script:hash.runspaces.clone()
        $script:hash.temphash | Where-Object {
            $_.runspace -eq $Null
        } | ForEach-Object {
            
            $script:hash.Runspaces.remove($_)
            
        }  
        [console]::Title = ("Remaining Runspace Jobs: {0}" -f ((@($script:hash.runspaces | Where-Object { $_.Runspace -ne $Null }).Count)))
                    
    } while ($more -AND $Wait)

    
    
}



$Script:Hash.SB_GetData = {

    Param($Script:Hash)
   
    $Script:hash.report = @()

    Do {
    
        Start-Sleep -Milliseconds 1
    
    } While ($Script:Hash.HandleStartRunSpaces.IsCompleted -eq $false)
    

    $Wait = $True

    Do {
        $more = $false  
        Foreach ($runspace in $Script:Hash.runspaces) { 
    
            If ($runspace.Runspace.isCompleted) {
                $handle = $runspace.powershell.EndInvoke($runspace.Runspace)
                $hash.report += $handle
                $runspace.powershell.dispose()
                $runspace.Runspace = $null
                $runspace.powershell = $null                 
            } 
            
            ElseIf ($runspace.Runspace -ne $null) {
                $more = $true
            }

        }
        $script:hash.runspaceCount = ($script:hash.Runspaces | Where-Object { $_.Runspace -ne $Null }).Count

        $Script:Hash.LabelInformation.ForeColor = "Black"
        $Script:Hash.LabelInformation.Text = "$($script:hash.runspaceCount) of $($hash.Usertotal) users left to process."
        If ($more -AND $Wait) {
            Start-Sleep -Milliseconds 100
            
        }   
        #Clean out unused runspace jobs
        $Script:Hash.temphash = $Script:Hash.runspaces.clone()
        $Script:Hash.temphash | Where-Object {
            $_.runspace -eq $Null
        } | ForEach-Object {
            
            $Script:Hash.Runspaces.remove($_)
            
        }  
        [console]::Title = ("Remaining Runspace Jobs: {0}" -f ((@($Script:Hash.runspaces | Where-Object { $_.Runspace -ne $Null }).Count)))
                    
    } while ($more -AND $Wait)

    
   
    #export report for listview
    $Script:hash.report
   
    $Script:Hash.PSRealMigratorListView = [powershell]::Create().AddScript($Script:hash.SB_GetListViewInformation).AddArgument($Script:Hash)
    $Script:Hash.PSRealMigratorListView.RunspacePool = $Script:Hash.runspacepool
    $Script:Hash.PSRealMigratorListView.BeginInvoke()

    
    
    $Script:Hash.PSRealMigratorCleanUpRunSpace = [powershell]::Create().AddScript($Script:hash.SB_CleanUpRunspaces).AddArgument($Script:Hash)
    $Script:Hash.PSRealMigratorCleanUpRunSpace.RunspacePool = $Script:Hash.runspacepool
    $Script:Hash.PSRealMigratorCleanUpRunSpace.BeginInvoke()
     
     
}




#---------------------------------------------------------------------------------------------------------------#
#
# Update/Export Report Section
#
#---------------------------------------------------------------------------------------------------------------#
$Script:Hash.SB_UpdateReport = {


    Param($hash)
    # Source variable contains the location of the RealMigrator Global Master tracker.
  
    $Script:Hash.Excel = New-Object -ComObject Excel.Application
    
    
    $Script:Hash.excel.Visible = $true
 
    $Script:Hash.Workbook = $Script:Hash.Excel.Workbooks.Open($Script:Hash.Source)
    Start-Sleep -Milliseconds 500

    
    
    $Script:Hash.worksheet = $Script:Hash.workbook.sheets.item('RealMigrator Tracker')


    if ($Script:Hash.worksheet.autofilter() -eq $null) {

        Write-Verbose "No filters to clear."

    }

    else {

        #Clear any filters from the worksheet
        $Script:Hash.worksheet.autofilter.showalldata()

    }

    # Get the date of the update
    $script:hash.dateupdate = Get-Date -Format "MM-dd-yyyy hh-mm-ss"

    #Variables for column search
    $Script:Hash.colUserPrincipalName = 1
    $Script:Hash.colLocation = 2
    $Script:Hash.colRMGroup = 4
    $Script:Hash.colFinalizationDate = 8
    $Script:Hash.colDisplayName = 9
    $Script:Hash.colClientID = 10
    $Script:Hash.colSizeInMB = 11
    $Script:Hash.colAutoExpandArchive = 12
    $Script:Hash.colStatus = 13
    $Script:Hash.colPSTImportStatus = 14
    $Script:Hash.colSizeSyncStatus = 15
    $Script:Hash.colPSTSyncTotal = 16
    $Script:Hash.colLastUpdate = 17
    $Script:Hash.colRegion = 18

    #Arrays to seperate duplicates from uniques
    $script:Hash.duplicates = @()
    $script:Hash.uniques = @()
    
    $Script:Hash.LabelGroupInformation.ForeColor = "Black"
    $Script:Hash.LabelGroupInformation.Text = "Searching for users with duplicate RealMigrator instances. Please wait..."
    $totalusersfromreport = $Script:Hash.Reporttoexport.Count
    $usercount = 1

    #Seperation loop. Add uniques to the unique array. If a duplicate value exists, add it to the duplicates array.
    foreach ($user in $hash.reporttoexport) {

        $Script:Hash.LabelInformation.Text = "Searching $usercount of $totalusersfromreport users for duplicate RealMigrator clients."
        
        if ($user.UserPrincipalName -eq $null -or $user.UserPrincipalName -eq "") {
            
            $usercount++
            
            Continue
        
        }

        
        #If the userprincipalname has a count greater than 1, add it to the duplicates array.
        if (($hash.reporttoexport -match $user.UserPrincipalName).Count -gt 1) {
        

            $script:hash.duplicates += $user

            $usercount++
        
        }
    
        #Add to the unique array.
        else {
        
            $script:hash.uniques += $user

            $usercount++
        
        } 
    
    }

    #Clear the labels
    $Script:Hash.LabelGroupInformation.Text = ""
    $Script:Hash.LabelDuplicateInformation.Text = ""

    #Create arrays for counting users. Duplicate and uniques.
    $script:hash.duplicatecounttotal = $Script:hash.duplicates.count
    $script:hash.uniquecounttotal = $Script:Hash.uniques.count
    $script:hash.duplicatecount = 1
    $script:hash.uniquecount = 1


    #Update unique users in the master tracker.
    foreach ($user in $script:hash.uniques) {
        
        $Script:Hash.LabelInformation.Text = "Updating $($script:hash.uniquecount) of $($script:hash.uniquecounttotal) unique users in the RealMigrator Tracker."

        #Variable creation
        $userprincipalname = $user.UserPrincipalName
        $userRMGroup = $user.RMGroup
        $userFinalizationDate = $user.FinalizationDate
        $userDisplayName = $user.DisplayName
        $userClientID = $user.ClientID
        $userSizeInMB = $user.SizeInMB
        $userAutoExpandArchive = $user.AutoExpandArchive
        $userStatus = $user.Status
        $userPSTImportStatus = $user.PSTImportStatus
        $usersyncsizestatus = $user.SizeSyncStatus
        $userPSTSyncTotal = $user.PSTSyncTotal
            

        # Search for the user in the source Excel Document. Search by userclientID to update correct clients in case of duplicates.
        $Found = $Script:Hash.WorkSheet.Cells.Find($userClientID)
        if ($found) {
            
            $row = $found.row
            
            #Update each cell found
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colRMGroup).Value2 = "$userRMGroup"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colFinalizationDate).Value2 = "$userFinalizationDate"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colDisplayName).Value2 = "$userDisplayName"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colClientID).Value2 = "$userClientID"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeInMB).Value2 = "$userSizeInMB"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colAutoExpandArchive).Value2 = "$userAutoExpandArchive"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colStatus).Value2 = "$userStatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTImportStatus).Value2 = "$userPSTImportStatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeSyncStatus).Value2 = "$usersyncsizestatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTSyncTotal).Value2 = "$userPSTSyncTotal" 
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colLastUpdate).Value2 = "$($script:hash.dateupdate)"
   

        }

        #If the client id is not found, then check if the user exists in the spreadsheet. If they do, then fill in the info as this is a new client.
        else {
        
            $Found = $Script:Hash.worksheet.Cells.Find($userprincipalname)

            if ($found) {
                
                $row = $found.row

                #Update each cell found
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colRMGroup).Value2 = "$userRMGroup"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colFinalizationDate).Value2 = "$userFinalizationDate"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colDisplayName).Value2 = "$userDisplayName"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colClientID).Value2 = "$userClientID"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeInMB).Value2 = "$userSizeInMB"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colAutoExpandArchive).Value2 = "$userAutoExpandArchive"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colStatus).Value2 = "$userStatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTImportStatus).Value2 = "$userPSTImportStatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeSyncStatus).Value2 = "$usersyncsizestatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTSyncTotal).Value2 = "$userPSTSyncTotal" 
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colLastUpdate).Value2 = "$($script:hash.dateupdate)"
            
            }
        
            
        }
        
        #Update the count.
        $script:hash.uniquecount++

    }
    #Update duplicates loop in tracker.
    foreach ($user in $script:hash.duplicates) {

        $Script:Hash.LabelDuplicateInformation.Text = "Updating $($script:hash.duplicatecount) of $($script:hash.duplicatecounttotal) duplicate users in the RealMigrator Tracker."
        $script:hash.duplicatecount++

        #Variable creation
        $userprincipalname = $user.UserPrincipalName
        $userRMGroup = $user.RMGroup
        $userFinalizationDate = $user.FinalizationDate
        $userDisplayName = $user.DisplayName
        $userClientID = $user.ClientID
        $userSizeInMB = $user.SizeInMB
        $userAutoExpandArchive = $user.AutoExpandArchive
        $userStatus = $user.Status
        $userPSTImportStatus = $user.PSTImportStatus
        $usersyncsizestatus = $user.SizeSyncStatus
        $userPSTSyncTotal = $user.PSTSyncTotal

        # Search for the user in the source Excel Document.
        $Found = $Script:Hash.WorkSheet.Cells.Find($userClientID)
        if ($found) {

    
            $row = $found.row
    
            #Update each cell found
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colRMGroup).Value2 = "$userRMGroup"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colFinalizationDate).Value2 = "$userFinalizationDate"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colDisplayName).Value2 = "$userDisplayName"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colClientID).Value2 = "$userClientID"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeInMB).Value2 = "$userSizeInMB"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colAutoExpandArchive).Value2 = "$userAutoExpandArchive"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colStatus).Value2 = "$userStatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTImportStatus).Value2 = "$userPSTImportStatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeSyncStatus).Value2 = "$usersyncsizestatus"
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTSyncTotal).Value2 = "$userPSTSyncTotal" 
            $Script:Hash.worksheet.cells.item($row, $Script:Hash.colLastUpdate).Value2 = "$($script:hash.dateupdate)"
    

        } 

        #If the clientID is not found, then the duplicate entry needs to be inserted into the Master Tracker.
        else {

            $upnFound = $Script:Hash.WorkSheet.Cells.Find($userprincipalname)
            $row = $upnFound.row
            #Insert method
            $script:hash.xlShiftDown = -4121


            if ($upnFound) {

                #Set variables from found UPN to copy to the new row.
                $userLocation = $Script:Hash.worksheet.cells.item($row, $script:hash.colLocation).Value2
                $userRegion = $Script:Hash.worksheet.cells.item($row, $script:hash.colRegion).Value2

                #select the entire row
                $selected = $Script:Hash.worksheet.cells.item($row, 1).entirerow()

                #Insert row, moving active row down one cell.
                $selected.Insert($xlshiftDown)

                #Update each cell found
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colUserPrincipalName).Value2 = "$userprincipalname"
                $Script:Hash.worksheet.cells.item($row, $script:hash.colLocation).Value2 = "$userLocation"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colRMGroup).Value2 = "$userRMGroup"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colFinalizationDate).Value2 = "$userFinalizationDate"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colDisplayName).Value2 = "$userDisplayName"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colClientID).Value2 = "$userClientID"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeInMB).Value2 = "$userSizeInMB"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colAutoExpandArchive).Value2 = "$userAutoExpandArchive"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colStatus).Value2 = "$userStatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTImportStatus).Value2 = "$userPSTImportStatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colSizeSyncStatus).Value2 = "$usersyncsizestatus"
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colPSTSyncTotal).Value2 = "$userPSTSyncTotal" 
                $Script:Hash.worksheet.cells.item($row, $Script:Hash.colLastUpdate).Value2 = "$($script:hash.dateupdate)"
                $Script:Hash.worksheet.cells.item($row, $script:hash.colRegion).Value2 = "$userRegion"
    

            }

            #If no UPN is found then skip that user as it's not in the tracker.
            else {
    
                Continue

            }


        }


    }


    #Begin excel document cleanup and close.
    $Script:Hash.LabelInformation.Text = ""
    $Script:Hash.LabelDuplicateInformation.Text = ""
    $Script:Hash.LabelGroupInformation.ForeColor = "Green"
    $Script:Hash.LabelGroupInformation.Text = "Updates complete."
    
    
    #Close the excel document
    $Script:Hash.workbook.close($true)
    
    $Script:Hash.excel.quit();

    
    $Script:Hash.LabelGroupInformation.Text = "Closing all instances of Excel."

    [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$hash.excel)
    [gc]::Collect()
    [gc]::WaitForPendingFinalizers()
    
    Remove-Variable excel -ErrorAction SilentlyContinue

    $Script:Hash.LabelGroupInformation.Text = "Excel document closed, allowing updates to be viewed." 
    
   
    

}




$Script:Hash.SB_ExportReport = {
    Param($hash)

    #Header columns for CSV
    $announcment1 = "Announcment 1 Sent"
    $annoucment2 = "Announcment 2 Sent"
    $rmdeploymentdate = "RM Deployment Date"
    $wave = "Wave"
    $location = "Location"


    $date = Get-Date -format "MM-dd-yyyy hh-mm"
        
    #Exports a csv file based on the selected group to your OneDrive folder.
    $script:hash.Reporttoexport | Select-object UserPrincipalName, $location, $wave, RMGroup, $rmdeploymentdate, $announcment1, $annoucment2, FinalizationDate, DisplayName, ClientID, SizeInMB, AutoExpandArchive, Status, PSTImportStatus, SizeSyncStatus, PSTSyncTotal, Comments | 
    Export-CSV -Path "$env:OneDrive\$($script:hash.GroupDisplayName)_Tracker_$date.csv" -Force -NoTypeInformation      
    

}


}